[Acwing算法基础] 3.6 Bellman-Ford

Bellman-Ford算法使用于含有负权边但不含负环的情况

dijkstra不能解决负权边是因为 dijkstra要求每个点被确定后st[j] = true,dist[j]就是最短距离了,之后就不能再被更新了(一锤子买卖),而如果有负权边的话,那已经确定的点的dist[j]不一定是最短了

考虑下面地一个例子:
在这里插入图片描述
dijkstra详细步骤

  • 初始dist[1] = 0
  • 找到了未标识且离源点1最近的结点1,标记1号点,用1号点更新其他所有点的距离,2号点被更新成dist[2] = 23号点被更新成dist[3] = 5
  • 找到了未标识且离源点1最近的结点2,标识2号点,用2号点更新其他所有点的距离,4号点被更新成dist[4] = 4
  • 找到了未标识且离源点1最近的结点4,标识4号点,用4号点更新其他所有点的距离,5号点被更新成dist[5] = 5
  • 找到了未标识且离源点1最近的结点3,标识3号点,用3号点更新其他所有点的距离,4号点被更新成dist[4] = 3
  • 结束
  • 得到1号点到5号点的最短距离是5,对应的路径是1 -> 2 -> 4 -> 5,并不是真正的最短距离

bellman-ford 算法思想

Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低。遍历k次(这个k是路径上经过的边的数量);每一次遍历,会遍历所有边,同时更新所有边的距离。

为什么要使用backup数组

更新距离的代码如下:

for (int i = 0; i < k; i ++ )
{
    memcpy(backup, dist, sizeof dist);
    for (int j = 0; j < m ; j ++ )
    {
        int a = edges[j].a, b = edges[j].b, w = edges[j].w;
        dist[b] = min(dist[b], backup[a] + w);  // 这里要使用backup数组防止串联
    }
}

串联的例子如下:(即:当k = 1时,1到3的距离应该是无穷大,但如果不使用backup数组,那么就会发生串联(3的距离就会变成2))
在这里插入图片描述

为什么是dist[n]>0x3f3f3f3f/2, 而不是dist[n]>0x3f3f3f3f

因为最后一个可能是无穷大减去若干个负数
在这里插入图片描述

完整代码

#include <cstring>
#include <algorithm>
#include <algorithm>

using namespace std;

const int N = 510, M = 10010;
int n, m, k;
int dist[N], backup[N];

struct Edge
{
    int a, b ,w;
}edegs[M];

int bellman_ford()
{
    // 初始化
    memset(dist, 0x3f3f3f3f, sizeof dist);
    dist[1] = 0;
    
    for (int i = 0; i < k; ++i)
    {
        // 只用上一次迭代的结果,那么就不会出现串联了
        // 1. backup数组防止串联
        memcpy(backup, dist, sizeof dist);  
        for (int j = 0; j < m; ++j)
        {
            int a = edegs[j].a, b = edegs[j].b, w = edegs[j].w;
            dist[b] = min(dist[b], backup[a] + w);
        }
    }
    
    // 这里不是无穷大, 而是比无穷大要小
    if (dist[n] > 0x3f3f3f3f / 2) return -1;
    return dist[n];
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    
    for (int i = 0; i < m; ++i)
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edegs[i] = {a, b, w};
    }
    
    int t = bellman_ford();
    
    if (t == -1)  // 说明最短路长度不存在
        puts("impossible");
    else printf("%d\n", t);
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Dijkstra算法Bellman-Ford算法都是用于解决图中单源最短路径问题的经典算法。 Dijkstra算法是一种贪心算法,用于求解从给定源节点到其他所有节点的最短路径。算法通过维护一个优先队列(或最小堆)来选择当前距离源节点最近的节点,并逐步扩展路径长度最短的节点。具体步骤包括:初始化源节点的距离为0,将其加入优先队列;从队列中取出距离最小的节点,并对其相邻节点进行松弛操作,更新其距离;重复上述步骤直到队列为空。 Bellman-Ford算法是一种动态规划算法,可以处理带有负权边的图。算法通过对所有边进行V-1轮松弛操作来逐步求解最短路径。具体步骤包括:初始化源节点距离为0,其他节点距离为正无穷;迭代V-1轮,对所有边进行松弛操作,即尝试通过更新边权值来缩短源节点到其他节点的距离;检测是否存在负权回路,如果存在则说明图中存在无限负权路径。 两者的主要区别在于: - Dijkstra算法要求图中边权值非负,而Bellman-Ford算法可以处理带负权边的情况。 - Dijkstra算法的时间复杂度为O((V + E)logV),其中V为节点数量,E为边数量;而Bellman-Ford算法的时间复杂度为O(VE),在稀疏图中效率较低。 选择使用哪种算法取决于具体的问题场景和图的特性。如果图中不存在负权边,且需要求解单源最短路径,Dijkstra算法是一个较好的选择。而如果图中可能存在负权边,并且需要检测负权回路,或者只需求解单源最短路径且图较稠密,可以考虑使用Bellman-Ford算法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东阳z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值