Bellman_Ford

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

使用范围

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

中心思想

对每个点进行 n − 1 n - 1 n1轮松弛
每一轮会遍历所有边,一共 n − 1 n - 1 n1 轮,时间复杂度也就是 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 n1 条边,即对每个点最多进行 n − 1 n - 1 n1 轮松弛可以得到最短距离。
如果超过 n − 1 n-1 n1 轮还有边可以被松弛,那么说明有源点 S S S 点能到达的负环。

实操步骤

  1. 枚举每个点 u u u
  2. 枚举 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]
  3. 循环上面过程 n − 1 n - 1 n1

代码

代码和思想一样
但略有不同。

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 k1轮的最短路,如果不这么做直接用 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 数组。

扩展

  1. 判断负环:据上面所说,我们最多 n − 1 n-1 n1 轮就能得出最短路径,也就说第 n n n 轮的时候不可能有边还能被松弛,如果有,则说明具有负环,可以无限松弛边长。这里还要注意一点,如果是发现第 n n n 轮没有边被松弛,不一定图上没有负环,可能只是源点 S S S 到不了而已,因此判断负环时应使用一个超级源点把所有点都连起来,边权为 0 0 0 ,这样才能完美地找出负环。
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值