基本上用不到的算法,和高精度一样,不常用,用到了又无可代替
常用于限制边数的最短路算法。
使用范围
可以处理任意边权的图,可以处理负环,可以判断负环。
时间复杂度
O
(
n
m
)
O(nm)
O(nm)。因为太慢了,在求最短路的时候基本用不到,但是它的优化版SPFA则大大优化了时间复杂度,算是最短路里最好用的算法,有很强的适应性。但近年来容易被卡。
中心思想
对每个点进行
n
−
1
n - 1
n−1轮松弛
每一轮会遍历所有边,一共
n
−
1
n - 1
n−1 轮,时间复杂度也就是
O
(
n
m
)
O(nm)
O(nm)
正确性证明
第
1
1
1 轮松弛得到的是起点到其他点,最多经过
1
1
1 条边的最短路径;第
n
n
n 轮得到的就是从起点到其他点,最多经过
n
n
n 条边点的最短路径。因为在
n
n
n 个点中,两个点之间的最短路径最多有
n
−
1
n - 1
n−1 条边,即对每个点最多进行
n
−
1
n - 1
n−1 轮松弛可以得到最短距离。
如果超过
n
−
1
n-1
n−1 轮还有边可以被松弛,那么说明有源点
S
S
S 点能到达的负环。
实操步骤
- 枚举每个点 u u u
- 枚举 u u u连接着的边 w w w 和点 j j j,并用和 d i s t [ u ] dist[u] dist[u] 和 w w w 更新 d i s t [ j ] dist[j] dist[j]
- 循环上面过程 n − 1 n - 1 n−1 次
代码
代码和思想一样
但略有不同。
int bellman_ford(int S, int T)
{
memset(dist, 0x3f, sizeof dist);
dist[S] = 0;
int k = n;
while ( -- k)
{
memcpy(g, dist, sizeof dist);
for (int u = 1; u <= n; u ++ )
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
dist[j] = min(dist[j], g[u] + w[i]);
}
}
return dist[T];
}
可以发现代码多了一个 g g g数组, g g g是 d i s t dist dist的复制数组,如果只是求最短路的话可以不用 g g g代替 d i s t dist dist,但如果考虑限制经过 k k k条边的话,必须加上。设当前为第 k k k轮,那么 g g g里面存的就是第 k − 1 k - 1 k−1轮的最短路,如果不这么做直接用 d i s t dist dist更新,可能会出现连续更新的现象,即在这轮用刚更新完的 d i s t dist dist去更新了其他点的 d i s t dist dist,就可能会打破边数的限制,在第k轮有经过 k + n k + n k+n条边的最短路径,导致算法错误。因此,有必要加上这个 g g g 数组。
扩展
- 判断负环:据上面所说,我们最多 n − 1 n-1 n−1 轮就能得出最短路径,也就说第 n n n 轮的时候不可能有边还能被松弛,如果有,则说明具有负环,可以无限松弛边长。这里还要注意一点,如果是发现第 n n n 轮没有边被松弛,不一定图上没有负环,可能只是源点 S S S 到不了而已,因此判断负环时应使用一个超级源点把所有点都连起来,边权为 0 0 0 ,这样才能完美地找出负环。