算法基础14 —— 图论入门之Bellman-Ford算法(Acwing 853 有边数限制的最短路)

回顾

迪杰斯特拉算法不能处理负的边权

Dijkstra 例2

Bellman-Ford

Bellman-Ford算法可以解决负权边。举例理解:
在这里插入图片描述

假设边给出的顺序是:

2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3

  • dis[v]为s到v的最短距离,则初始时dis数组如下图所示:

在这里插入图片描述
第一次松弛:

  • 当加入2->3这条边时,dis[2] + 2 = ∞ + 2仍然为∞,故不更新dis数组
  • 当加入1->2这条边时,dis[1] + (-3) = 0 + (-3) = -3 < ∞,故1->2的距离更新为-3,更新dis[2] = -3
    在这里插入图片描述
  • 当加入1->5这条边时,dis[1] + 5 = 0 + 5 < ∞,1->5的距离更新为5,更新dis[5] = 5
    在这里插入图片描述
  • 当加入4->5这条边时,dis[4] + 2 = 无穷 + 2 > 5,1->5的距离不变,故不需更新dis[5]
  • 最后,当加入3->4这条边时,dis[3] + 3 = 无穷 + 3,不更新dis[4]
    在这里插入图片描述
  • 以上为第一轮松弛

第二次松弛:

  • 当再次加入2->3这条边时,dis[2] + 2 = -3 + 2 = -1 < ∞,故更新dis[3] = -1
    在这里插入图片描述
  • 当再次加入1->2这条边时,dis[1] + (-3) = 0 + (-3) = -3 = dis[2],故不需要更新
  • 当再次加入1->5这条边时,dis[1] + 5 = 0 + 5 = 5 = dis[5],故不需要更新
  • 当再次加入4->5这条边时,dis[4] + 2 = ∞ + 2 > dis[4],故不需要更新
  • 最后,当再次加入3->4这条边时,dis[3] + 3 = -1 + 3 = 2 < dis[4],故不需要更新dis[4] = 2
    在这里插入图片描述

第三次松弛:

  • 当第三次加入2->3这条边时,dis[2] + 2 = -3 + 2 = -1 = dis[3],故不需更新

  • 当第三次加入1->2这条边时,dis[1] + (-3) = 0 + (-3) = -3 = dis[2],故不需要更新

  • 当第三次加入1->5这条边时,dis[1] + 5 = 0 + 5 = 5 = dis[5],故不需要更新

  • 当第三次加入4->5这条边时,dis[4] + 2 = 2 + 2 = 4 < dis[5],故更新dis[5] = 4
    在这里插入图片描述

  • 最后,当第三次加入3->4这条边时,dis[3] + 3 = -1 + 3 = 2 = dis[4],故不需要更新

第三次松弛: 不会更新dis数组

结论:至多经过n - 1次(上个例子只更新了n - 2次)之后,dis数组会更新为下图:
在这里插入图片描述

  • 对于n个结点的图,需要松弛多少遍呢?至多n - 1遍,因为一条最短路径的长度最多为n - 1条边。在实际操作中,该算法经常会在未达到n - 1轮松弛前就已经计算出最短路径。鉴于此就有优化的方法 —— SPFA算法。
算法模板
void Bellman-ford(int s)//求出源点s到其余各点的最短距离
{
    memset(dis,0x3f,sizeof dis);//初始化距离为∞
    dis[s] = 0;
    for (int i = 1;i <= n - 1;i++)//松弛n - 1遍
    {
        int check = 0;
        for (int j = 1;j <= m;j++)//枚举每一条边
        {
            //判断能否通过加入j这条边,使得到达v[j]号顶点的距离变短
            //u[j]是j的起点,v[j]是j的终点,w[j]是j边的长度
            if (dis[v[j]] > dis[u[j]] + w[j])
            {
                dis[v[j]] = dis[u[j]] + w[j];
                check = 1;
            }
        }
        //循环之后发现没有更新dis数组,则可以提前结束循环
        if (check == 0) break;
    }
}

在这里插入图片描述

算法总结

时间复杂度:O(NE),N是顶点数,E是边数
算法实现:设s为起点,dis[v]即为s到v的最短距离,pre[v]为v前驱。w[j]是边j的长度,且j连接u、v。初始化:dis[s] = 0,dis[v] = ∞(v≠s),pre[s] = 0
伪代码

For (i = 1; i <= n - 1; i++)
	For (j = 1; j <= m; j++)//枚举所有边
   		if (dis[u[j]] + w[j] < dis[v[j]])//u[j]、v[j]分别是这条边连接的两个起点与终点
    	{
       		dis[v[j]] = dis[u[j]] + w[j];
       		pre[v[j]] = u[j];
  		}

在这里插入图片描述

虽然Bellman-Ford算法可以求出存在负边权情况下的最短路径,却无法解决存在负权回路的情况。负权回路是指边权之和为负数的一条回路,上图中②-④-⑤-③-②这条回路的边权之和为-3。在有负权回路的情况下,从1到6的最短路径是多少?答案是无穷小,因为我们可以绕这条负权回路走无数圈,每走一圈路径值就减去3,最终达到无穷小。所以说存在负权回路的图无法求出最短路径,Bellman-Ford算法可以在有负权回路的情况下输出错误提示

例题以及AC代码

Acwing 853 有边数限制的最短路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值