当前位置: 首页 > article >正文

【算法】Bellman-Ford单源最短路径算法(附动图)

目录

一、性质

二、思路

三、有边路限制的最短路


一、性质

  • 适用于含有负权边的图(Dijkstra不适用)

  • 更简单,但效率慢

  • 如果对应路径存在负权回路则没有最短路径(可用于判断图中是否存在负权回路)

  • 相比于spfa,若最短路径有边数限制则只能使用Bellman-Ford

负权回路:图中带环且环中所有边的权重和为负


二、思路

Bellman-Ford算法的思路十分简单粗暴

与Dijkstra类似的,我们用一个dist数组存放源节点到任意节点的最短路径

首先,在求最短路径前,我们需要将源节点到自己的距离初始化为0,到其他节点的距离初始化为最大值

然后,我们遍历所有的边

对于一条边,我们这里可以直接用一个结构体存储其信息,例如:

struct edge{
    int a, b, w;
}edges[M];

其中a为有向边出发的节点,b为有向边指向的节点,w为边的权重

遍历所有的边,如果 dist[a]+w < dist[b] ,则更新最短路径

以同样的方式一共循环n次,我们就能得到源节点到任意节点的最短路径

 

有时不一定非要循环n次才能得到最优解,例如上面我们只循环了3次。但最坏的情况下,如果我们的图是这样的呢?

有n个节点,但只有n-1条边,我们至少循环n-1次才能将所有节点更新

那我们不能只循环n-1次吗?

前面提到,Bellman-Ford算法可用于检测图中是否存在负权回路,具体是如何检测的?靠的就是这多出来的一次循环

首先解释一下为何路径中存在负权回路则不存在最短路径,因为我们如果在这个负权回路中一直走,那么路径长度不就一直在变小直到负无穷吗?

可以看到,在上面最坏的情况下,我们也只需要循环n-1次就能将源节点到每个节点的最短路径求出来,而最后一次循环中不会有节点被更新。但如果存在负权回路,则最后一次循环中一定还会有节点被更新。通过判断我们就可以知道图中是否存在负权回路。

即使每次遍历边的顺序不同也不会影响最后的结果,但每次循环中dist数组中的值可能不同。因为如果遍历边的顺序与边的指向一致,我们可以连续更新多个节点,例如:

可以看到dist的数组更新是串联的,即一个位置更新可能影响到其他位置,如果我们没有对最短路径的边数进行限制的话就无需多虑,但如果最短路径有边数限制,那我们就需要一个额外的备份数组来辅助节点更新


三、有边路限制的最短路

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 510, M = 10010;

struct edge{
    int a, b, w;
}edges[M];

int n, m, k;
int dist[N], backup[N]; 
//最短路径有边数限制,所以我们还需要一个备份数组

int bellman_ford()
{
    //初始化dist数组
    memset(dist, 0x3f, sizeof dist); 
    dist[1] = 0; 
    
    //最短路径不能超过k条边,因此我们循环k次
    for(int i = 0;i < k;i++) 
    {
        //将上一次循环中dist数组的结果存入backup数组
        memcpy(backup, dist, sizeof dist); 
        for(int j = 0;j < m;j++)
        {
            int a, b, w;
            a = edges[j].a, b = edges[j].b, w = edges[j].w;
            dist[b] = min(dist[b], backup[a] + w); //用backup数组来更新dist数组
        }
    }
    return dist[n];
}

int main()
{
    cin >> n >> m >> k;
    for(int i = 0;i < m;i++)
    {
        int a, b, w;
        cin >> a >> b >> w;
        edges[i] = {a,b,w}; //存边
    }
    
    int ans = bellman_ford(); //Bellman-Ford算法
    
    if(ans > 0x3f3f3f3f / 2) cout << "impossible"; 
    //返回值过大不符合预期,可以判断不存在满足条件的路径
    else cout << ans;
    return 0;
}

如有错误,欢迎在评论区指出

完.


http://www.kler.cn/news/368151.html

相关文章:

  • 【算法】递归系列:206.反转链表(两种递归实现)
  • SSM学习day01 JS基础语法
  • 【Linux 从基础到进阶】数据库高可用与灾备方案
  • 《决策思维:人人必备的决策口袋书》
  • 如何修改IP地址:全面指南
  • 基于neo4j的学术论文关系管理系统
  • 【LeetCode:263. 丑数 + 数学】
  • 【已解决,含泪总结】非root权限在服务器上配置python和torch环境,代码最终成功训练(一)
  • 设计模式——过滤器模式
  • 脚本-把B站缓存m4s文件转换成mp4格式
  • vue通过JSON文件生成KML文件源码
  • There is no screen to be resumed matching xxx【解决方案、screen、原因分析】
  • 《2024中国泛娱乐出海洞察报告》解析,垂直且多元化方向发展!
  • linux驱动—注册驱动分析
  • 使用Python计算相对强弱指数(RSI)进阶
  • HarmonyOS NEXT 应用开发实战(八、知乎日报List列表下拉刷新及上滑加载更多分页的实现)
  • Vue引入高德地图自定义信息窗体绑定点击事件无效解决方案
  • anaconda 创建环境失败 解决指南
  • 【刷题10】2024.10.26
  • Spark 广播变量(Broadcast Variable)原理及源码分析
  • 绝了,这款播放器让发烧友疯狂种草,堪称音乐神器
  • 力扣876:链表的中间结点
  • 安全知识见闻-网络安全热门证书
  • SpringBoot技术栈在宠物用品交易网站中的应用
  • php后端学习,Java转php
  • 智能合约开发中的LP分红系统