int n, m; int h[N], w[M], e[M], ne[M], idx; int dist[N], cnt[N];//dist严格意义说不是用来存最短距离的。所以dist中元素初始值并不是那么重要。我们就拿dist中元素全部初始化为0来举例分析一下,如果图中全部是正权重,那么if就压根不会执行。cnt也不会更新。如果图中全是负权重,但没有负环,其实就跟求最短路比较类似了,但dist存的也不完全是源点1到各个点的最短距离,这里有这么两种情况(比如1->2,1->3,2->3,这时dist中元素就代表了源点1到各点的最短距离,因为如果这样,2,3开始不入队迟早也会入队,这样就退化成了,只开始入队1,其他点不入队的情况,算的是1到各点的最短距离。另外一种是可能存在1->3,2->3,1到不了2,这样此时dist[3],就代表了1,2分别为起点到3的最短距离。如果存在连通分支的情况其实就是前面两个情况的结合。)第一种情况很显然就跟单源最短路一样,不存在负环的前提下最短路径的长度也就是n-1,一旦大于等于n就说明有负环。第二种情况可以把例如3这个点分别看成以1为源点,以2为源点求最短路。此时cnt代表的就是两个求最短路的过程中dist的更新次数。如果有n个点,1->n,但到不了2,2->n,但1可以到其他所有点,这样就变成以1为源点和以2为源点最短路,以1的角度看就是n-1个点的最短路,在这个最短路中cnt最多达到n-2,同样2->n最短路中cnt最多是1,相加也最对是n-1。类似的分析,如果1到不了很多点,可以看成多个最短路,在每个最短路中看cnt最大值,综合所有cnt更新会发现cnt最大也就是n-1(在没有负回路的前提下)有不同联通分支分析与第二种情况分析类似。 //如果既有负权重,又有正权重,对于dist全初始为0的情况来说,正权重其实就起不到作用,这里可以认为正权重的边不存在。把正权重的边删掉后,就又变成了全部是负权重的情况。回到了上面的分析。综上,如果回路中不存在负回路,那么cnt最多也就是n-1,只有存在负权重,cnt才会大于等于n //所以结合上述分析,我们可以知道cnt[j]各个起源点到j点的最短长度的和 bool st[N];
voidadd(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; }
boolspfa() { queue<int> q;
for (int i = 1; i <= n; i ++ ) { st[i] = true; q.push(i); }//这里是因为如果和正常的spfa一样的话,只能判断从源点1可以到达所有的点的路径中有没有负环。但如果源点有到不了的点,比如不在一个联通分支,这样就会有点不会入队,这样就没法判断这个点到其他点的路径中是否存在负环,即也无法判断图中是否有负环,所以开始让所有点都入队
while (q.size()) { int t = q.front(); q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; cnt[j] = cnt[t] + 1;
if (cnt[j] >= n) returntrue; if (!st[j]) { q.push(j); st[j] = true; } } } }
returnfalse; }
Floyd
for(k =1;k<=n;k++)
for(i =1;i<=n;i++)
for(j =1;j<=n;j++)
d[i,j] = min(d[i,j],d[i,k] + d[k,j]);
d[i,j]存的就是最短路长度
1 2 3 4 5 6 7
voidfloyd() { for (int k = 1; k <= n; k ++ ) for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) d[i][j] = min(d[i][j], d[i][k] + d[k][j]); }