您现在的位置是:主页 > news > 做购物平台网站需要多少资金/广告联盟看广告赚钱
做购物平台网站需要多少资金/广告联盟看广告赚钱
admin2025/6/4 2:31:25【news】
简介做购物平台网站需要多少资金,广告联盟看广告赚钱,有哪些网站做电子元器件比较好,巨耀网站建设公司文章目录参考文献最短路推论Bellman-ford 算法SPFA错误的证明方法卡法参考文献 https://www.cnblogs.com/jason2003/p/7224580.html 一下代码均抄自此大佬的博客。 最短路推论 对于一个最短路,最多经过n−1n-1n−1条边,如果超过这个,则必然…
文章目录
- 参考文献
- 最短路推论
- Bellman-ford 算法
- SPFA
- 错误的证明方法
- 卡法
参考文献
https://www.cnblogs.com/jason2003/p/7224580.html
一下代码均抄自此大佬的博客。
最短路推论
对于一个最短路,最多经过n−1n-1n−1条边,如果超过这个,则必然重复经过一个点,则必然存在负环,证毕。
Bellman-ford 算法
通过上面,我们不难知道,我们可以用dp思想,设f[i]f[i]f[i]表示111到iii在最多经过jjj条边的最短路径,那么,我们只需要在添上一条边就是j+1j+1j+1条边了。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int dis[10010];
int origin[10010],destination[10010],value[10010];//刚刚说过的三个数组
int n,m;
void Bellman_ford(int a)
{memset(dis,88,sizeof(dis));//赋初始值dis[a]=0;for(int i=1;i<=n-1;i++)//更新n-1次for(int j=1;j<=m;j++)//更新每一条边dis[destination[j]]=min(dis[destination[j]],dis[origin[j]]+value[j]);//判断是否更新}
int main()
{cin>>n>>m;for(int i=1;i<=m;i++)cin>>origin[i]>>destination[i]>>value[i];Bellman_ford(1);for(int i=1;i<=n;i++)cout<<dis[i]<<" ";
}
不难看出,时间复杂度是:O(nm)O(nm)O(nm)。
SPFA
不难发现,如果f[i]f[i]f[i]在第jjj层没有被更新,那么他在第j+1j+1j+1层不会去更新人,因此可以用队列储存被更新的,一般思路就是对于队列中的这个点而言,如果他可以更新xxx,那么就把xxx扔进队列里面,这样子的话,不难发现时间复杂度是O(nm)O(nm)O(nm)的,因为只是把没可能更新的给剔除了罢了。
但是我们发现如果xxx正在队列里面,我们就可以不扔进去,为什么?看这么一个队列:
假设i,x,ji,x,ji,x,j的disdisdis的层数为kkk,并且i,ji,ji,j都可以更新xxx并产生k+1k+1k+1层的xxx的话,那么在xxx更新完后,jjj 依旧会产生k+1k+1k+1层的xxx,但关键就是有时候不会存在jjj,所以我们不会产生k+1k+1k+1层的xxx,况且就是存在了,第kkk层的xxx的disdisdis竟然是k+1k+1k+1的(因为dis是一维数组,没有储存层数信息)!!!会不会产生影响呢?
首先,层数增大最多导致disdisdis的减少,减少了,何乐而不为呢?
我们再看有没有可能经过xxx和虚xxx之间的点更新过导致虚xxx可以引发更多的更新?
由于dis经过更新只会不断的变小的,所以xxx原本更新不了的,现在也更新不了。(当然如果xxx到虚xxx之间有数字可以更新xxx的话就当我没说,不过这种情况会产生新的xxx放到队尾。)
如果你看了上面还是对这个优化绝对是正优化的证明感觉还不是很严谨,我给你简化一下过程,对于一个未优化过的队列,两个xxx之间不存在一个点的disdisdis可以更新xxx,那么后面的xxx的作用可以被前面的代替,且第kkk层xxx的disdisdis的更新作用可以被第k+1k+1k+1层的xxx所代替,所以可以直接把后面的xxx删掉并且用前面的xxx代替后面的xxx(这也是为什么disdisdis只用一维的原因)。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
using namespace std;
int dis[10010];
int book[10010];
int origin[10010],destination[10010],value[10010];
int n,m;
int total;
int next[10010],head[10010];
void adl(int a,int b,int c)//邻接表
{total++;origin[total]=a;destination[total]=b;value[total]=c;next[total]=head[a];head[a]=total;
}
void Bellman_ford(int a)
{memset(book,0,sizeof(book));//book[i]表示i号点是否在队列里memset(dis,88,sizeof(dis));queue <int> q;q.push(a);book[a]=1;dis[a]=0;while(!q.empty())//当队列不为空时更新{for(int e=head[q.front()];e;e=next[e])//枚举队首点相邻的每一个点{if(dis[destination[e]]>dis[origin[e]]+value[e]){dis[destination[e]]=dis[origin[e]]+value[e];if(book[destination[e]]==0){q.push(destination[e]);//将更新的这一个点入队book[destination[e]]=1;}}}q.pop();//弹出队首元素}}
int main()
{cin>>n>>m;for(int i=1;i<=m;i++){int a,b,c;cin>>a>>b>>c;adl(a,b,c);} Bellman_ford(1);for(int i=1;i<=n;i++)cout<<dis[i]<<" ";
}
错误的证明方法
当然,时间复杂度还是不变,有人说可以利用一条边最多引起一次全图范围的更新来证明(我机房的一个大佬提出的),但是这个是否成立呢?我们称一条边所引发的全图范围的更新为涟漪(像水波一样)看这个图:
虽然打上了边的方向,但是个人觉得无向图也可以用这个图。
我们只需要合理调控边的边权,就可以让1号点旁边的绿边从1号点出发引发一次涟漪,但是,正是由于走了绿边,所以这个涟漪的更新速度会比111或333号点的disdisdis向外更新的速度慢一。所以涟漪和111或333号点的disdisdis向外更新均会在222号点的绿边引发一次涟漪,所以222号点周围的绿边竟然引发了两次涟漪!!!!
往后不断重复,一条边甚至可以产生更多的涟漪!!!
当然如果我有问题欢迎指出
所以应该是不能用这个方法证明的。
卡法
现在给出一种比较神奇的卡法(点多的随机稠密图好像也可以卡)。
调一下边的权值,争取上面小,上面大,上面的不断更新下面的,应该是可以卡到nm4\frac{nm}{4}4nm的(当然我没细算,我又不会时间复杂度分析)。
至此,我终于记录下了我某个平常晚上的思考。