搜索与图论 - bellman-ford 算法

news2024/11/23 19:12:19

文章目录

  • 一、为什么 Dijkstra 算法不适用于含负权的图
    • 1. 理论推导
    • 2. 实例演示
      • 2.1 详细步骤
      • 2.2 结果
  • 二、bellman-ford 算法
    • 1. 简介
    • 2. 基本思路
    • 3. 简单举例
    • 4. bellman-ford 算法具体实现过程详见例题有边数限制的最短路。
  • 三、bellman-ford 算法例题——有边数限制的最短路
    • 具体实现
      • 1. 样例演示
      • 2. 实现思路
      • 3. 代码注解
      • 4. 实现代码

一、为什么 Dijkstra 算法不适用于含负权的图

  • 关于 Dijkstra 算法的讲解详见搜索与图论- Dijkstra 算法。

1. 理论推导

  • Dijkstra 算法在运行过程中维持的关键信息是一组节点集合 S ,从源节点 S 到该集合中每个节点之间的最短路径已经被找到。算法重复从节点集合 V-S 中选择最短路径估计最小的节点 u ,将 u 加入到集合 S ,然后对所有从 u 出发的边进行操作。
  • 当把一个节点选入集合 S 时,即意味着已经找到了从源点到这个点的最短路径,但若存在负权边,就与这个前提矛盾,可能会出现得出的距离加上负权后比已经得到 S 中的最短路径还短。(无法回溯)

2. 实例演示

  • 如下图为例,一共有 5 个点,也就说要循环 5 次,确定每个点的最短距离。
    在这里插入图片描述

2.1 详细步骤

  • (1) 初始 dist[1] = 0,1 号点距离起点 1 的距离为 0 。
  • (2) 找到了未标识且离起点 1 最近的 1 号点,标记 1 号点,用 1 号点更新和它相连点的距离,2 号点被更新成 dist[2] = 2,3 号点被更新成 dist[3] = 5。
  • (3) 找到了未标识且离起点 1 最近的 2 号点,标记 2 号点,用 2 号点更新和它相连点的距离,4 号点被更新成 dist[4] = 4。
  • (4) 找到了未标识且离起点 1 最近的 4 号点,标记 4 号点,用 4 号点更新和它相连点的距离,5 号点被更新成 dist[5] = 5。
  • (5) 找到了未标识且离起点 1 最近的 3 号点,标记 3 号点,用 3 号点更新和它相连点的距离,4 号点被更新成 dist[4] = 3。

2.2 结果

  • 得到 Dijkstra 算法在图中走出来的最短路径是1 -> 2 -> 4 -> 5,算出 1 号点到 5 号点的最短距离是 2 + 2 + 1 = 5,然而还存在一条路径是1 -> 3 -> 4 -> 5,该路径的长度是5 + (-2) + 1 = 4。
  • 因此,Dijkstra 算法失效。
  • 我们可以发现如果有负权边的话 4 号点经过标记后还可以继续更新,但此时 4 号点已经被标记过了,所以 4 号点不能被更新了,只能一条路走到黑。
  • 当用负权边更新 4 号点后 5 号点距离起点的距离我们可以发现可以进一步缩小成 4。
  • 总结:Dijkstra 不能解决负权边是因为 Dijkstra要求每个点被确定后,dist[j] 就是最短距离了,之后就不能再被更新了(一锤子买卖),而如果有负权边的话,那已经确定的点的 dist[j] 不一定是最短了,可能还可以通过负权边进行更新。

二、bellman-ford 算法

  • 针对负权边的问题,我们采用 bellman-ford 算法进行解决。

1. 简介

  • 贝尔曼-福特算法(Bellman-Ford)是由理查德·贝尔曼(Richard Bellman)和莱斯特·福特创立的,求解单源最短路径问题的一种算法。其优于 Dijkstra 算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高。但它也有特别的用处,一般用于实现通过 m 次迭代求出从起点到终点不超过 m 条边构成的最短路径。

2. 基本思路

  • bellman-ford 算法的思路也很简单,直接就是两层循环,内层循环所有边,外层循环就是循环所有边的次数,这个外层循环次数一般是题目控制的。时间复杂度是 O(n*m)。
  • 首先n次迭代,每一次循环所有边。这里用 a,b,w 表示存在一条从 a 走到 b 的边,权重是 w。
  • 这里存边方式有很多种,可以用邻接表,结构体等。遍历所有边的时候更新一下其他点的距离,和 Dijkstra 算法类似,用当前这个点更新和它相连的点距离起点的距离。
  • 这里使用 dist 数组表示每个点到起点的距离,那么更新操作就是 dist[b]=min(dist[b],dist[a]+w) ,这样就可以更新和 a 相连的 b 点距离起点的距离,这个更新的过程就是”松弛操作”。
  • 在循环所有边的时候,每一次循环要先把 dist 数组备份一下。

3. 简单举例

  • 如下图例子,红色边表示权重,求一下 1 号点到 5 号点的最短路径。
    在这里插入图片描述
  • 从 1 号点走到 2 号点,2,3,4 号点围成了一个圈,圈的总权重是 5 + (-4) + (-2) = (-1),那么转一圈长度就会减一,因此我们可以转无穷多圈,转无穷多圈总长度就会变成负无穷,出圈的话还是负无穷。
  • 所以说图中存在负权回路的话,从 1 号点到 n 号点的距离就会变成负无穷,就不存在了。
  • bellman-ford 算法是可以判断图中存不存在负权回路。
  • 首先上面的迭代次数是有实际意义的,比如我们迭代了 k 次,那么我们求的最短距离就是从 1 号点经过不超过 k 条边走到 n 号点的最短距离。所以在第 n 次迭代的时候又更新了某些边的话,就说明路径中一定存在环,并且是负权回路。因为第 n 次迭代在不存在负权回路的情况下是遍历到第 n 号点了,后面是没有点了,如果还能更新,说明路径中存在回路,而且是负权回路。

4. bellman-ford 算法具体实现过程详见例题有边数限制的最短路。

三、bellman-ford 算法例题——有边数限制的最短路

题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出impossible

输入格式

第一行包含三个整数 n,m,k。
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
点的编号为 1∼n。

输出格式

输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。
如果不存在满足条件的路径,则输出 impossible

数据范围

1 ≤ n,k ≤ 500
1 ≤ m ≤ 10000
1 ≤ x,y ≤ n
任意边长的绝对值不超过 10000。

输入样例

3 3 1
1 2 1
2 3 1
1 3 3

输出样例

3

具体实现

1. 样例演示

  • 首先,输入 n=3,m=3,k=1;表示从 1 号点到 3 号点最多经过 1 条边的最短距离。下面共有 3 条边。
  • 第一条边是从 1 号点到 2 号点,边长为 1。
  • 第二条边是从 2 号点到 3 号点,边长为 1。
  • 第三条边是从 1 号点到 3 号点,边长为 3。
  • 具体情况如下图所示:
    在这里插入图片描述
  • 要求从 1 号点出发到 3 号点最多经过 1 条边的最短距离,通过图很容易就可以看出,最短距离就是 3,也就是 1 号点直接到 3 号点的距离。
  • 因此,输出为 3。

2. 实现思路

  • 由 bellman-ford 算法的步骤,也就是两层循环,外层循环题目控制的是 k,也就是 1,内层循环所有的边。也就说只会进行一次迭代。
  • 看一下内层循环的过程,首先一共有三条边,所以内层循环要循环三次。我们看一下内层循环后的结果。注意:这里是没有备份 dist 数组的结果。
1号点2号点3号点
内层循环第一次执行0
内层循环第二次执行01
内层循环第三次执行012
  • 由上表可得,如果没有备份 dist 数组的话,最短距离就变成了 2。内层循环只迭代了一次,但是在更新的过程中会发生”串联”。比如说先更新 2 号点,然后用 2 号点更新 3 号点距离起点的距离,这样就发生了”串联”,3 号点不能被 2 号点更新,这样就不满足题目要求了,因为题目要求最多不经过 1 条边。
  • 为了解决“串联”问题的发生,保证更新的时候只用上一次循环的结果就行,所以先备份一下。
  • 备份之后 last 数组存的就是上一次循环的结果,用上一次循环的结果来更新距离。
  • 所以写成 dist[b]=min(dist[b],last[a]+w) 来更新距离。

3. 代码注解

  • int back[N];备份数组防止串联。
  • memset(dist,0x3f,sizeof dist);初始化距离。
  • memcpy(last, dist, sizeof dist);将 dist 数组的内容复制到 last 数组当中,数据类型为 dist 的数据类型。
  • dist[n] > 0x3f3f3f3f / 2;是因为在图当中,可能会存在负权边,假设 5 号点到 n 号点的距离为 -2,5 号点是 0x3f3f3f3f,n 号点也是 0x3f3f3f3f,就有可能将 n 号点给更新为 0x3f3f3f3f-2,在数据量较小的情况下没有影响。
  • 其他代码注解标识在实现代码当中。

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

const int N = 510, M = 10010;


//使用结构体存储边的信息
struct Edge
{
    int a, b, c;
}edges[M];

int n, m, k;
int dist[N];
int last[N];

void bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);

    dist[1] = 0;
    for (int i = 0; i < k; i ++ )
    {
        memcpy(last, dist, sizeof dist);
        //更新所有的边
        for (int j = 0; j < m; j ++ )
        {
            auto e = edges[j];
            //使用backup:避免给a更新后立马更新b, 这样b一次性最短路径就多了两条边出来
            dist[e.b] = min(dist[e.b], last[e.a] + e.c);
        }
    }
}

int main()
{
    cin >> n >> m >> k;

    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        edges[i] = {a, b, c};
    }

    bellman_ford();

    if (dist[n] > 0x3f3f3f3f / 2)
    {
        puts("impossible");
    }
    else 
    {
        cout << dist[n] << endl;
    }
    system("pause");
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91164.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

仓库24代 “ CK_Label_v24

产品型号 CK_Label_v24 尺寸 124x90x12mm&#xff08;不含安装支架&#xff09; 屏幕尺寸 4.2 inch 显示技术 电子墨水屏显示 显示区域面积 (mm) 84.8(H) x 63.6(V) 分辨率 400*300 像素密度 120dpi 显示颜色 黑/白 外观颜色 白色&灰外圏 按键 3 指示灯…

【C++】STL标准模板库

目录 一、概念 STL的四种基本组件 容器vector 迭代器iterator 函数对象function object 算法algorithm 二、使用 容器vector的使用 泛型程序设计&#xff1a; 所谓泛型程序设计就是编写不依赖于具体数据类型的程序。C中&#xff0c;模板就是泛型程序设计的主要…

一次疑似 JVM native 内存泄漏的排查实录

最近开发同学反馈&#xff0c;某定时任务服务疑似有内存泄漏&#xff0c;整个进程的内存占用比 Xmx 内存大不少&#xff0c;而且看起来是缓慢上升的&#xff0c;做了下面这次分析&#xff0c;包括下面的内容&#xff1a; 分析 JVM native 内存的一些常见思路内存增长了&#x…

关于Arduino连接L298N供电问题

关于Arduino连接L298N供电问题 查看原文 该L298N板声称有一个5V稳压器为Arduino供电&#xff0c;在这种情况下&#xff0c;您可以使用单个电源&#xff0c;并让电机板为Arduino供电。 关于为Arduino和电机提供动力有两种思想流派&#xff1a; 使用两个独立的电源&#xff0…

NumPy 的使用

NumPy&#xff08;Numerical Python&#xff09;是Python 语言的一个扩展程序库&#xff0c;支持大量的维度数组与矩阵运算&#xff0c;同时也针对数组运算提供大量的数学函数库。 NumPy 的前身 Numeric 最早由 Jim Hugunin 与其他协作者共同开发&#xff0c;2005 年&#xff0…

百万千万爆款视频的脚本是怎么写出来的?两套模板教你做同款

那些百万千万爆款视频的脚本是怎么写出来的&#xff1f;两套模板教你做同款。 每天都能刷到百万赞的短视频&#xff0c;看看自己的视频点赞量&#xff0c;失落是一种感觉&#xff0c;其实你也可以做出优秀的爆款文案。 今天给大家介绍两种短视频脚本模板&#xff0c;大家可以…

idea手动创建干净的maven项目,很简单

大家好&#xff0c;今天我们分享使用idea开发工具创建干净的maven项目 这是Maven的官网&#xff1a; 点一下就可以 首先&#xff0c;我们来了解一下什么是Maven&#xff0c;就是说关于Maven这个东西你要知道的是 1.Maven是一个跨平台&#xff08;在很多平台上都可以使用&…

B4:Unity制作Moba类游戏——小兵AI系统

若想取得战争的胜利&#xff0c;必先控好兵线。 ———— 麦克阿瑟 是时候让敌人经历一下我们兵线的洗礼。 ———— 拿破仑 在LOL对局中&#xff0c;职业选手对兵线的控制可以说是达到了“运筹帷幄之中,决胜千里之外”。其实普通玩家只要控好兵线&#xff0c;在对线中一样可以…

Java Servlet详解(补充,极其重要)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;JAVA开发者…

SwiftUI 中列表行(List Row)展开和收起无动画或动画诡异的解决

文章目录 问题现象问题分析1. 为什么 List 行展开与收起没有动画效果?2. 第一种解决方法3. 另一种巧妙的解决总结结束语问题现象 SwiftUI 中展开(expand)和收起(collapse)列表行(List Row)是一个常见的操作,不过默认来说这样的操作不会有动画效果: 如上图所示,我们为…

粒子滤波算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

CpG ODN——艾美捷ODN 1826 (TLRGRADE)说明书

艾美捷CpG ODN系列——ODN 1826 (TLRGRADE)&#xff1a;具有硫代磷酸酯骨架的CpG寡脱氧核苷酸&#xff08;B型&#xff09;。小鼠TLR9&#xff08;Toll样受体9&#xff09;的特异性配体。 艾美捷CpG ODN 丨ODN 1826 (TLRGRADE)化学性质&#xff1a; 备选名称&#xff1a;CpG-B…

Suspense组件

先上官网&#xff1a;https://cn.vuejs.org/guide/built-ins/suspense.html 注意一下 <Suspense> 是一项实验性功能。它不一定会最终成为稳定功能&#xff0c;并且在稳定之前相关 API 也可能会发生变化。 在使用了之后在浏览器控制台会有如下打印&#xff0c;至少目前是…

【大数据】有关zookeeper的问题

如图&#xff0c;启动zookeeper失败&#xff0c;输入 zkServer.sh start-foreground 查看失败原因 Invalid config&#xff0c;我得知是配置文件出了问题&#xff0c;但是检查配置文件没有发现错误 最终在配置文件末尾配置参数结尾发现了未删除的空格 将三个节点配置文件中的…

C/C++ 和 Java的编译运行机制比较 个人理解

计算机程序语言按程序的执行方式可分为编译型语言和解释性语言。 编译型语言是指使用专用的编译器&#xff0c;针对某操作系统将高级语言源代码一次性地翻译成可被该系统硬件执行的机器码(包括机器指令和操作数&#xff09;&#xff0c;并包装成该系统所能识别的可执行程序的格…

同事开源我的微服务深度实践笔记到 GitHub,短短 3 天竟吸粉 1W+

说Spring成就了Java&#xff0c;Spring是Java程序员必修课之一&#xff0c;应该没人反对吧&#xff1f;前几年面试最常问的且可以顺利拿到高薪的技能是Spring&#xff0c;随着Spring体系的壮大&#xff0c;除非你在简历上添加Spring Boot和Spring Cloud的技能&#xff0c;才可以…

Kubernetes——Debug Static Pod

1. 问题背景 注意&#xff0c;我这里的Static Pod并非Kubernetes的Static Pod&#xff0c;而是需要把想要Debug的程序放到Delve环境中重新打包一个镜像。因为还有另外一种场景&#xff0c;那就是我们需要不重启Running Pod&#xff0c;为了和这种方式区分&#xff0c;才以此为…

彻底卸载并重装Anaconda环境与Python的方法

本文介绍在Windows平台下&#xff0c;彻底删除Anaconda环境与其自带Python版本&#xff0c;并进行重新安装的方法。 最近&#xff0c;由于原有Anaconda环境中的部分第三方库出现了冲突的情况&#xff0c;且基于“Anaconda Prompt (anaconda3)”也无法升级Anaconda与相关库了&am…

《超新星纪元》

《超新星纪元》 关于作者 刘慈欣&#xff0c;髙级工程师&#xff0c;科普作家&#xff0c;被誉 为"中国当代科幻第一人"。自上世纪90年代开始&#xff0c;他一边在发电厂担任计算机工程师&#xff0c;一边利用业余时间出版了13本小说集&#xff0c;连续数年获得中国…

这是一篇讲解用户行为分析的推荐书单和总结

写在前面 技术文延迟了 本来计划参加活动的还有一篇&#xff0c;应该是一篇技术翻译文&#xff0c;但是那篇文章太难了&#xff0c;看我过我以往文章的同学&#xff0c;应该能理解&#xff0c;我的文章很少有3000字数以下的&#xff0c;而且如果不是来自谷歌&#xff08;主要…