Bellman_Ford

基本上用不到的算法,和高精度一样,不常用,用到了又无可代替
常用于限制边数的最短路算法。

使用范围

可以处理任意边权的图,可以处理负环,可以判断负环。
时间复杂度 \(O(nm)\)。因为太慢了,在求最短路的时候基本用不到,但是它的优化版SPFA则大大优化了时间复杂度,算是最短路里最好用的算法,有很强的适应性。但近年来容易被卡。

中心思想

对每个点进行\(n - 1\)轮松弛
每一轮会遍历所有边,一共 \(n - 1\) 轮,时间复杂度也就是 \(O(nm)\)

正确性证明

\(1\) 轮松弛得到的是起点其他点,最多经过 \(1\) 条边的最短路径;第 \(n\) 轮得到的就是从起点到其他点,最多经过 \(n\) 条边点的最短路径。因为在 \(n\) 个点中,两个点之间的最短路径最多有 \(n - 1\) 条边,即对每个点最多进行 \(n - 1\) 轮松弛可以得到最短距离。
如果超过 \(n-1\) 轮还有边可以被松弛,那么说明有源点 \(S\) 点能到达的负环。

实操步骤

  1. 枚举每个点 \(u\)
  2. 枚举\(u\)连接着的边 \(w\) 和点 \(j\),并用和 \(dist[u]\)\(w\) 更新 \(dist[j]\)
  3. 循环上面过程 \(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\)\(dist\)的复制数组,如果只是求最短路的话可以不用\(g\)代替\(dist\),但如果考虑限制经过\(k\)条边的话,必须加上。设当前为第\(k\)轮,那么\(g\)里面存的就是第\(k - 1\)轮的最短路,如果不这么做直接用\(dist\)更新,可能会出现连续更新的现象,即在这轮用刚更新完的\(dist\)去更新了其他点的\(dist\),就可能会打破边数的限制,在第k轮有经过\(k + n\)条边的最短路径,导致算法错误。因此,有必要加上这个\(g\)数组。

扩展

  1. 判断负环:据上面所说,我们最多 \(n-1\) 轮就能得出最短路径,也就说第 \(n\) 轮的时候不可能有边还能被松弛,如果有,则说明具有负环,可以无限松弛边长。这里还要注意一点,如果是发现第 \(n\) 轮没有边被松弛,不一定图上没有负环,可能只是源点 \(S\) 到不了而已,因此判断负环时应使用一个超级源点把所有点都连起来,边权为 \(0\) ,这样才能完美地找出负环。
posted @ 2024-05-12 21:02  blind5883  阅读(3)  评论(0编辑  收藏  举报