代码随想录算法训练营第五十九天 | Bellman_ford 算法精讲

news2024/12/25 9:28:47

目录

Bellman_ford 算法精讲

思路

什么叫做松弛

模拟过程

方法一: Bellman_ford算法


Bellman_ford 算法精讲

  • 题目链接:卡码网:94. 城市间货物运输 I
  • 文章讲解:代码随想录 

某国为促进城市间经济交流,决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市,通过道路网络连接,网络中的道路仅允许从某个城市单向通行到另一个城市,不能反向通行。

网络中的道路都有各自的运输成本和政府补贴,道路的权值计算方式为:运输成本 - 政府补贴。

权值为正表示扣除了政府补贴后运输货物仍需支付的费用;权值为负则表示政府的补贴超过了支出的运输成本,实际表现为运输过程中还能赚取一定的收益。

请找出从城市 1 到城市 n 的所有可能路径中,综合政府补贴后的最低运输成本。

如果最低运输成本是一个负数,它表示在遵循最优路径的情况下,运输过程中反而能够实现盈利。

城市 1 到城市 n 之间可能会出现没有路径的情况,同时保证道路网络中不存在任何负权回路。

负权回路是指一系列道路的总权值为负,这样的回路使得通过反复经过回路中的道路,理论上可以无限地减少总成本或无限地增加总收益。

输入描述

第一行包含两个正整数,第一个正整数 n 表示该国一共有 n 个城市,第二个整数 m 表示这些城市中共有 m 条道路。

接下来为 m 行,每行包括三个整数,s、t 和 v,表示 s 号城市运输货物到达 t 号城市,道路权值为 v(单向图)。

输出描述

如果能够从城市 1 到连通到城市 n, 请输出一个整数,表示运输成本。如果该整数是负数,则表示实现了盈利。如果从城市 1 没有路径可达城市 n,请输出 "unconnected"。

输入示例:

6 7
5 6 -2
1 2 1
5 3 1
2 5 2
2 4 -3
4 6 4
1 3 5

思路

本题依然是单源最短路问题,求 从 节点1 到节点n 的最小费用。 但本题不同之处在于 边的权值是有负数了

从 节点1 到节点n 的最小费用也可以是负数,费用如果是负数 则表示 运输的过程中 政府补贴大于运输成本。

在求单源最短路的方法中,使用dijkstra 的话,则要求图中边的权值都为正数。

我们在 dijkstra朴素版 中专门有讲解:为什么有边为负数 使用dijkstra就不行了。

本题是经典的带负权值的单源最短路问题,此时就轮到Bellman_ford登场了,接下来我们来详细介绍Bellman_ford 算法 如何解决这类问题。

该算法是由 R.Bellman 和L.Ford 在20世纪50年代末期发明的算法,故称为Bellman_ford算法。

Bellman_ford算法的核心思想是 对所有边进行松弛n-1次操作(n为节点数量),从而求得目标最短路

什么叫做松弛

看到这里,估计大家都比较晕了,为什么是 n-1 次,那“松弛”这两个字究竟是个啥意思?

我们先来说什么是 “松弛”。

《算法四》里面把这个操作叫做 “放松”, 英文版里叫做 “relax the edge”

所以大家翻译过来,就是 “放松” 或者 “松弛” 。

但《算法四》没有具体去讲这个 “放松” 究竟是个啥? 网上很多题解也没有讲题解里的 “松弛这条边,松弛所有边”等等 里面的 “松弛” 究竟是什么意思?

这里我给大家举一个例子,每条边有起点、终点和边的权值。例如一条边,节点A 到 节点B 权值为value,如图:

minDist[B] 表示 到达B节点 最小权值,minDist[B] 有哪些状态可以推出来?

状态一: minDist[A] + value 可以推出 minDist[B] 状态二: minDist[B]本身就有权值 (可能是其他边链接的节点B 例如节点C,以至于 minDist[B]记录了其他边到minDist[B]的权值)

minDist[B] 应为如何取舍。

本题我们要求最小权值,那么 这两个状态我们就取最小的

if minDist[B] > minDist[A] + value: 
    minDist[B] = minDist[A] + value

也就是说,如果 通过 A 到 B 这条边可以获得更短的到达B节点的路径,即如果 minDist[B] > minDist[A] + value,那么我们就更新 minDist[B] = minDist[A] + value ,这个过程就叫做 “松弛” 。

以上讲了这么多,其实都是围绕以下这句代码展开:

if minDist[B] > minDist[A] + value:
    minDist[B] = minDist[A] + value

这句代码就是 Bellman_ford算法的核心操作

以上代码也可以这么写:minDist[B] = min(minDist[A] + value, minDist[B])

如果大家看过代码随想录的动态规划章节,会发现 无论是背包问题还是子序列问题,这段代码(递推公式)出现频率非常高的。

其实 Bellman_ford算法 也是采用了动态规划的思想,即:将一个问题分解成多个决策阶段,通过状态之间的递归关系最后计算出全局最优解。

(如果理解不了动态规划的思想也无所谓,理解我上面讲的松弛操作就好)

那么为什么是 n - 1次 松弛呢

这里要给大家模拟一遍 Bellman_ford 的算法才行,接下来我们来看看对所有边松弛 n - 1 次的操作是什么样的。

我们依然使用minDist数组来表达 起点到各个节点的最短距离,例如minDist[3] = 5 表示起点到达节点3 的最小距离为5

模拟过程

初始化过程。

起点为节点1, 起点到起点的距离为0,所以 minDist[1] 初始化为0

如图:

其他节点对应的minDist初始化为max,因为我们要求最小距离,那么还没有计算过的节点 默认是一个最大数,这样才能更新最小距离。

对所有边 进行第一次松弛: (什么是松弛,在上面我已经详细讲过)

以示例给出的所有边为例:

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

接下来我们来松弛一遍所有的边。

边:节点5 -> 节点6,权值为-2 ,minDist[5] 还是默认数值max,所以不能基于 节点5 去更新节点6,如图:

(在复习一下,minDist[5] 表示起点到节点5的最短距离)

边:节点1 -> 节点2,权值为1 ,minDist[2] > minDist[1] + 1 ,更新 minDist[2] = minDist[1] + 1 = 0 + 1 = 1 ,如图:

边:节点5 -> 节点3,权值为1 ,minDist[5] 还是默认数值max,所以不能基于节点5去更新节点3 如图:

边:节点2 -> 节点5,权值为2 ,minDist[5] > minDist[2] + 2 (经过上面的计算minDist[2]已经不是默认值,而是 1),更新 minDist[5] = minDist[2] + 2 = 1 + 2 = 3 ,如图:

边:节点2 -> 节点4,权值为-3 ,minDist[4] > minDist[2] + (-3),更新 minDist[4] = minDist[2] + (-3) = 1 + (-3) = -2 ,如图:

边:节点4 -> 节点6,权值为4 ,minDist[6] > minDist[4] + 4,更新 minDist[6] = minDist[4] + 4 = -2 + 4 = 2

边:节点1 -> 节点3,权值为5 ,minDist[3] > minDist[1] + 5,更新 minDist[3] = minDist[1] + 5 = 0 + 5 = 5 ,如图:


以上是对所有边进行一次松弛之后的结果。

那么需要对所有边松弛几次才能得到 起点(节点1) 到终点(节点6)的最短距离呢?

对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离

上面的距离中,我们得到里 起点达到 与起点一条边相邻的节点2 和 节点3 的最短距离,分别是 minDist[2] 和 minDist[3]

这里有录友疑惑了 minDist[3] = 5,分明不是 起点到达 节点3 的最短距离,节点1 -> 节点2 -> 节点5 -> 节点3 这条路线 距离才是4。

注意我上面讲的是 对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离,这里 说的是 一条边相连的节点。

与起点(节点1)一条边相邻的节点,到达节点2 最短距离是 1,到达节点3 最短距离是5。

而 节点1 -> 节点2 -> 节点5 -> 节点3 这条路线 是 与起点 三条边相连的路线了。

所以对所有边松弛一次 能得到 与起点 一条边相连的节点最短距离。

那对所有边松弛两次 可以得到与起点 两条边相连的节点的最短距离。

那对所有边松弛三次 可以得到与起点 三条边相连的节点的最短距离,这个时候,我们就能得到到达节点3真正的最短距离,也就是 节点1 -> 节点2 -> 节点5 -> 节点3 这条路线。

那么再回归刚刚的问题,需要对所有边松弛几次才能得到 起点(节点1) 到终点(节点6)的最短距离呢

节点数量为n,那么起点到终点,最多是 n-1 条边相连。

那么无论图是什么样的,边是什么样的顺序,我们对所有边松弛 n-1 次 就一定能得到 起点到达 终点的最短距离。

其实也同时计算出了,起点 到达 所有节点的最短距离,因为所有节点与起点连接的边数最多也就是 n-1 条边。

截止到这里,Bellman_ford 的核心算法思路,大家就了解的差不多了。

共有两个关键点。

  • “松弛”究竟是个啥?
  • 为什么要对所有边松弛 n - 1 次 (n为节点个数) ?

那么Bellman_ford的解题解题过程其实就是对所有边松弛 n-1 次,然后得出得到终点的最短路径。

方法一: Bellman_ford算法

# 每个节点松弛n-1次,得到最近的距离

def main():
    n,m = map(int,input().split())

    grid = []

    for _ in range(m):
        s,t,v = map(int,input().split())
        grid.append([s,t,v])

    start = 1
    end = n
    minDist = [float('inf')] * (n+1)

    minDist[start] = 0
    for i in range(1,n):
        # Python版Bellman_ford算法,使用update及时判断min_dist停止更新的时机,直接break,不会超时。
        update = False
        for edge in grid:
            from_city = edge[0]
            to_city = edge[1]
            val = edge[2]
            if minDist[from_city] != float('inf') and val + minDist[from_city] < minDist[to_city]:
                minDist[to_city] = val + minDist[from_city]
                update = True
        if not update:
            break

    print('unconnected') if minDist[end] == float('inf') else print(minDist[end])

if __name__=="__main__":
    main()

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

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

相关文章

maya-vray渲染蒙版

要用一个叫vrayMulWrapper的材质球&#xff0c;把alpha Conterbution调到-1&#xff0c;勾选matte surface启用蒙版物体。

【C++题解】1406. 石头剪刀布?

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1406. 石头剪刀布&#xff1f; 类型&#xff1a;二维数组 题目描述&#xff1a; 石头剪刀布是常见的猜拳游戏。石头胜剪刀&#xff0c;剪刀胜布&#xff0c;布胜石头。如果两个人出…

数据库索引底层数据结构之B+树MySQL中的页索引分类【纯理论干货,面试必备】

目录 1、索引简介 1.1 什么是索引 1.2 使用索引的原因 2、索引中数据结构的设计 —— B树 2.1 哈希 2.2 二叉搜索树 2.3 B树 2.4 最终选择之——B树 2.4.1 B树与B树的对比(面向索引)【面试题】 3、MySQL中的页 3.1 页的使用原因 3.2 页的结构 3.2.1 页文件头和页文件…

Unity实战案例全解析:PVZ 植物卡片状态分析

Siki学院2023的PVZ免费了&#xff0c;学一下也坏 卡片状态 卡片可以有三种状态&#xff1a; 1.阳光足够&#xff0c;&#xff08;且cd好了可以种植&#xff09; 2.阳光不够&#xff0c;&#xff08;cd&#xff1f;好了&#xff1a;没好 &#xff08;三目运算符&#xff09;&…

Linux | 探索 Linux 信号机制:信号的产生和自定义捕捉

信号是 Linux 操作系统中非常重要的进程控制机制&#xff0c;用来异步通知进程发生某种事件。理解信号的产生、阻塞、递达、捕捉等概念&#xff0c;可以帮助开发者更好地编写健壮的应用程序&#xff0c;避免由于未处理的信号导致程序异常退出。本文将带你从基础概念开始&#x…

基于SpringBoot+Vue的牙科就诊管理系统(带1w+文档)

基于SpringBootVue的牙科就诊管理系统(带1w文档) 基于SpringBootVue的牙科就诊管理系统(带1w文档) 伴随着互联网发展&#xff0c;现今信息类型愈来愈多&#xff0c;信息量也非常大&#xff0c;那也是信息时代的缩影。近些年&#xff0c;电子元器件信息科学合理发展的趋势变的越…

【React】React18.2.0核心源码解读

前言 本文使用 React18.2.0 的源码&#xff0c;如果想回退到某一版本执行git checkout tags/v18.2.0即可。如果打开源码发现js文件报ts类型错误请看本人另一篇文章&#xff1a;VsCode查看React源码全是类型报错如何解决。 阅读源码的过程&#xff1a; 下载源码 观察 package…

C# System.BadImageFormatException问题及解决

C# System.BadImageFormatException问题 出现System.BadImageFormatException 异常有两种情况&#xff1a;程序目标平台不一致&引用dll文件的系统平台不一致。 异常参考 BadImageFormatException 程序目标平台不一致&#xff1a; 项目>属性>生成&#xff1a;x86 …

学LabVIEW编程,看编程书有些看不懂怎么办?

自学LabVIEW编程时&#xff0c;如果发现编程书籍内容难以理解&#xff0c;可以尝试以下几种方式来提高学习效果&#xff1a; 1. 从基础入手&#xff0c;逐步深入&#xff1a; LabVIEW是一种基于图形化编程的工具&#xff0c;不同于传统的编程语言&#xff0c;因此从基础概念开…

linux 操作系统下cupsenable命令介绍和使用案例

linux 操作系统下cupsenable命令介绍和使用案例 cupsenable 命令是 Linux 操作系统中用于启用 CUPS&#xff08;通用打印服务&#xff09;打印机的命令。它允许用户将指定的打印机重新启用&#xff0c;从而使其可以接受新的打印作业 cupsenable 命令概述 基本语法 bash cup…

LEAN 赋型唯一性(Unique Typing)之 n-provability 注解

《LEAN 赋型唯一性&#xff08;Unique Typing&#xff09;之 证明过程简介》 中&#xff0c;梳理了赋型唯一性&#xff08;Unique Typing&#xff09;牵涉的概念及相关推论与证明&#xff0c;此篇文章就先介绍 n-provability 的概念&#xff0c;记 ⊢ₙ 。其围绕的是赋型规则&a…

PHP创意无限一键生成小程序名片生成系统源码

创意无限&#xff0c;一键生成 —— 小程序名片生成系统&#xff0c;开启你的个性化商务新时代&#xff01; 一、告别千篇一律&#xff0c;拥抱个性化名片 你还在使用那些千篇一律的传统纸质名片吗&#xff1f;是时候做出改变了&#xff01;现在有了“创意无限一键生成小程序名…

Cisco Catalyst 9000 Series Switches, IOS XE Release 17.15.1 ED

Cisco Catalyst 9000 Series Switches, IOS XE Release 17.15.1 ED 思科 Catalyst 9000 交换产品系列 IOS XE 系统软件 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-catalyst-9000/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&…

如何计算光伏在安装过程中的损耗程度?

光伏系统在实际安装和运营过程中&#xff0c;会受到多种因素的影响&#xff0c;导致电能损耗。这些损耗包括线缆损耗、逆变器效率、组件品质、灰尘积累、入射角损失等。 一、光伏系统损耗的分类 光伏系统的损耗大致可以分为以下几类&#xff1a; 1、线缆损耗&#xff1a;光伏…

响应式网站和自适应网站有什么区别?

响应式网站和自适应网站在技术基础、用户体验以及开发成本等方面存在显著差异。具体分析如下&#xff1a; 响应式网站和自适应网站有什么区别? 技术基础 响应式网站&#xff1a;通过CSS3的媒体查询&#xff08;Media Query&#xff09;来检测设备屏幕尺寸&#xff0c;并加载…

全网最适合入门的面向对象编程教程:49 Python函数方法与接口-函数与方法的区别和lamda匿名函数

合集 - Python面向对象编程(51) 1.可能是全网最适合入门的面向对象编程教程&#xff1a;Python实现-嵌入式爱好者必看&#xff01;06-232.全网最适合入门的面向对象编程教程&#xff1a;00 面向对象设计方法导论06-243.全网最适合入门的面向对象编程教程&#xff1a;01 面向对…

make 程序规定的 makefile 文件的书写语法(5)

&#xff08;40&#xff09;接着学习自动变量&#xff0c;就是 make 程序执行时&#xff0c;自动定义和产生的变量&#xff0c;来描述 makefile 文件&#xff0c;可以直接拿来用&#xff1a; 补充 D 与 F 的使用&#xff0c;前者只获得目录&#xff0c;后者只获得文件名&#x…

【C++算法】滑动窗口

长度最小的子数组 题目链接&#xff1a; 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/minimum-size-subarray-sum/description/ 算法原理 代码步骤&#xff1a; 设置left0&#xff0c;right0设置sum0&#xff0c;len0遍历l…

深度学习-13-小语言模型之SmolLM的使用

文章附录 1 SmolLM概述1.1 SmolLM简介1.2 下载模型2 运行2.1 在CPU/GPU/多 GPU上运行模型2.2 使用torch.bfloat162.3 通过位和字节的量化版本3 应用示例4 问题及解决4.1 attention_mask和pad_token_id报错4.2 max_new_tokens=205 参考附录1 SmolLM概述 1.1 SmolLM简介 SmolLM…

六西格玛咨询:石油机械制造企业的成本控制与优化专家

一、石油机械制造行业现状及主要困扰 随着全球能源需求的日益增长&#xff0c;石油开采和生产设备需求不断增加&#xff0c;石油机械制造行业在过去数十年里得到了迅猛发展。然而&#xff0c;石油机械制造作为一个高度复杂且技术密集的行业&#xff0c;也面临着多重挑战。首先…