94. 城市间货物运输 I

Bellman_ford 算法

Bellman_ford 算法 与 dijkstra算法 相比通用性更强。

  dijkstra算法解决不了负权边的问题,因为Dijkstra基于贪心策略一旦一个节点被从队列中取出(标记为已解决),它就假定已经找到了到达该节点的最短路径。如果存在负权边,可能会有一条通过负权边的路径去到达该节点,这个负权边可以让一个已经被标记为已解决的节点的距离变得更小,但算法不会再回头去检查它,从而导致错误的结果。

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

什么是松弛?

松弛是Bellman_ford 算法的实现基础。

松弛就是一个“检查并更新”的过程:检查是否存在一条通过某个中间节点的更短路径,如果存在,就更新当前的最短路径估计。其实就是更新min_List数组,来判断从起点出发到达下一个点是否有更短的路径。

代码实现:

# 对边 (u, v) 进行松弛操作
if dist[u] + weight(u, v) < dist[v]:dist[v] = dist[u] + weight(u, v)prev[v] = u # 可选:记录v是从u来的

为什么是n-1次?

因为任何不包含负权环的最短路径最多包含 n-1 条边。n-1 次松弛足以保证找到所有可能的最短路径。 即n个点的话,从起点出发到终点的最短路径只需要经过n-1条边。

Bellman_ford 算法为什么能解决负权边的问题?

为什么dijkstra算法不能解决负权边的问题,关键是visited数组的设置导致了即使有负权边通向某个结节点,但由于visited中该节点之前被访问过了,因此无法更新。

Bellman-Ford 算法不依靠visited数组,其实现就是从每个点出发去松弛n-1次,来得到从起点出发经过该节点到达下一个节点时的最短路径。如下

dist 初始化为 [ ∞, 0, ∞, ∞, ∞, ∞ ],n=5,因此需要松弛4次。

第一次松弛:

  • 边 1→2 (100): dist[1] + 100 = 0 + 100 = 100 < ∞ → 更新 dist[2] = 100
  • 边 1→3 (1): dist[1] + 1 = 0 + 1 = 1 < ∞ → 更新 dist[3] = 1
  • 边 2→3 (-300): dist[2] + (-300) = 100 - 300 = -200 < 1 → 更新 dist[3] = -200
  • 边 3→4 (1): dist[3] + 1 = -200 + 1 = -199 < ∞ → 更新 dist[4] = -199
  • 边 4→5 (1): dist[4] + 1 = -199 + 1 = -198 < ∞ → 更新 dist[5] = -198

第二次松弛

  • 边 1→2 (100): 0 + 100 = 100 = 100 → 无变化
  • 边 1→3 (1): 0 + 1 = 1 > -200 → 无变化
  • 边 2→3 (-300): 100 - 300 = -200 = -200 → 无变化
  • 边 3→4 (1): -200 + 1 = -199 = -199 → 无变化
  • 边 4→5 (1): -199 + 1 = -198 = -198 → 无变化

......

经过4次松弛后就得到了更新后的min_List数组。从树的生成角度来理解这两个算法,如下:

Code

if __name__ == "__main__":city_size, road_size = map(int, input().split())length = city_size + 1graph = [[] for _ in range(length)] ##graph 存储了length个空数组min_List = [float('inf')] * lengthmin_List [1] = 0for _ in range(road_size):s, t, v = map(int, input().split())graph[s].append([t,v])for _ in range(city_size):      ## 松弛n-1次updated = False      ## 不一定要松弛完n-1次,如果发现不再更新了,那就可以退出了for i in range(1, length):       ## 遍历每个点cur_node = i       if len(graph[i]) == 0:       ## 没有下一个节点 continuefor j in range(len(graph[i])):  ## 每个节点进行了n-1次循环。即n-1次松弛next_node = graph[i][j][0]value = graph[i][j][1]dis = min_List[cur_node] + value  # 从起点出发经历当前节点到下一个节点if min_List[cur_node] != float('inf') and dis < min_List[next_node]:min_List[next_node] = disupdated = Trueif updated == False:   # 在一次松弛过程中,遍历完所有节点后发现min_List没有更新break if min_List[-1] == float('inf'):print("unconnected")else:print(min_List[-1])

注意:非常重要的点:

  1. 松弛是外循环,遍历节点在内循环。
  2. 一次松弛是对每个节点之间的边关系进行松弛,而不是一次松弛对当前节点下的所有边关系进行松弛,要搞清楚这二者的区别。

前者才是在找最优解,后者问题很大,看似像dijkstra算法,但dijkstra算法是每次选当前离起点距离最小的未访问节点去进行遍历,后者现在的问题是最短路径一定要是按编号顺序走去到吗?如下图:

正确路径应该是1-3-2-4,后者是顺序遍历节点,当遍历到3时,此时虽然能更新节点2的min_List值,但由于后续你不会再去遍历节点2了,因此找不到最优解。

Bellman_ford 队列优化算法

优化思路:

没用队列优化的Bellman_ford算法时间复杂度是O(N*E), N是松弛次数,E是节点数目。由于上述Bellman_ford算法我们通过数组来有序地存储了节点从小到大的边的关系,因此本身也算是优化了。

如果没有用数组有来序存储了节点从小到大的边的关系,而是直接根据题意提供的边的顺序进行松弛,那就会出现一个问题。当我们遍历当前节点如5时,想要松弛得到min_List[6],但当前min_List[5] 为 max,因为 1 -> 2 这条边的关系放在后面,因此还没松弛到,此时那你松弛5 -> 6这条边就是在做无用功。边的提供顺序如下:

252
56-2
531
121
135

此时,你在根据这个顺序进行松弛操作就做了很多无用功,因为你要算下一个节点到起点的最短距离,但距离起点更近的点此时候都不知道,那松弛就是在做无用功。如下,你要求5,3,6到1的最近距离,那关键边1-2, 1-3 都没传进来,此时松弛就是无用功。

Code

from collections import dequeif __name__ == "__main__":city_size, road_size = map(int, input().split())length = city_size + 1graph = [ [] for _ in range(length)]min_List = [float('inf')] * lengthmin_List [1] = 0for _ in range(road_size):s, t, v = map(int, input().split())graph[s].append([t,v])     ## 直接按提供的边的顺序进行存储queue = deque()queue.append(1)     ## 先将起点存入,queue的顺序为从起点开始到终点的经过过的节点顺序visited = [False] * lengthvisited[1] = Truefor i in range(city_size):   ## 松弛n-1次 updated = Falsewhile queue:cur_node = queue.popleft()visited[cur_node] = False       ## 方便下次松弛时,visited数组更改为False了,这样你才能对上层节点的边进行更新。如果上层节点松弛后更新了,那后续就添加到队列中继续进行松弛操作了。for t, v in graph[cur_node]:     ## 直接从上一个节点下的链表进行查找下一个点if min_List[cur_node] + v < min_List[t]:min_List[t] = min_List[cur_node] + v    ## 对边进行松弛if visited[t] == False:     ## 下一个点不在队列里面visited[t] = True       ## 对松弛后更新的边进行标记,让其只在队列中出现一次,以确保后续不重复松弛queue.append(t)     ## A -> B, 现在是从A去处理B之前的节点边关系,后面就是从B出发去处理下一个节点之前的边的节点关系updated = Trueif updated == False:        ## 松弛后发现min_List不变了,那后续没必要松弛了breakif min_List[-1] == float('inf'):print("unconnected")else:print(min_List[-1])

注意: 

  1. visited的设置是为了队列中每一个元素都是唯一的,保证不重复添加相同的元素,比如当前队列中已经有6这个节点,如果你没有visited数组,后续在松弛的过程又对6的min_list数组进行了更新,又往队列中添加了一次节点6,导致队列中出现两次节点6,后续就对节点6进行了重复的松弛的操作。visited的设置主要是为了优化,屏蔽掉的话也可以通过,但当边的关系很多很复杂时,此时可能就会存在超时的情况。
  2. 在时间复杂度上,该优化方法的时间复杂度为O(N*k),N表示节点数量,k表示每个节点的出度。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用Prometheus + Grafana + Loki构建一个现代化的云原生监控系统

如何使用 Prometheus + Grafana + Loki 构建一个现代化的云原生监控系统。这套组合被誉为监控领域的“瑞士军刀”,功能强大且生态极佳。 一、核心组件概念介绍 在搭建之前,深刻理解每个组件的角色和职责至关重要。 1. Prometheus(指标监控与时序数据库) 角色:系统的“核…

JavaScript Object 操作方法及 API

一、对象创建方式1.字面量创建&#xff08;最常用&#xff09;const obj { name: "张三", age: 25 };2.构造函数创建const obj new Object(); obj.name "李四";3.Object.create()&#xff08;指定原型&#xff09;const proto { greet: () > "…

pta乙级题目day1

第1天&#xff1a;输入输出与运算&#xff08;6题&#xff09;1001 害死人不偿命的(3n1)猜想&#xff08;基础运算&#xff09;★1006 换个格式输出整数&#xff08;格式化输出&#xff09;★1016 部分AB&#xff08;数字提取&#xff09;★★1046 划拳&#xff08;多输入处理&…

在VSCode中配置.NET项目的tasks.json以实现清理、构建、热重载和发布等操作

在 VS Code 中配置 .NET 开发任务的完整指南 引言 重要提醒&#xff1a;对于 .NET 开发&#xff0c;强烈推荐使用 Visual Studio&#xff0c;它提供了最完整和稳定的开发体验。如果你像我一样"蛋疼"想要尝试 VS Code&#xff0c;请确保安装了 C# 开发扩展包&#x…

EmEditor文本编辑器v25.3.0专业版,专业文本编辑,高亮显示,无限撤消

[软件名称]: EmEditor文本编辑器v25.3.0专业版 [软件大小]: 37.7 MB [软件大小]: 夸克网盘 | 百度网盘 软件介绍 EmEditor 是一款功能强大且非常实用的文本编辑器。它启动速度快&#xff0c;完全可以替代 Windows 自带的记事本&#xff0c;轻松应对日常文本编辑任务。它对 …

【spring security】权限管理组件执行流程详解

&#x1f3af; 权限管理组件执行流程详解 &#x1f3d7;️ 组件架构图 ┌─────────────────────────────────────────────────────────────┐ │ HTTP请求 …

redis怎么保障双写一致性

redis做为缓存&#xff0c;mysql的数据如何与redis进行同步呢&#xff1f;&#xff08;双写一致性&#xff09;候选人&#xff1a;嗯&#xff01;就说我最近做的这个项目&#xff0c;里面有xxxx&#xff08;根据自己的简历上写&#xff09;的功能&#xff0c;需要让数据库与red…

异常值检测:孤立森林模型(IsolationForest)总结

目录一、前言二、孤立森林算法2.1 算法简介2.2 基本原理2.3 算法步骤2.4 异常分数计算方式2.5 python调用方式三、python代码示例四、小结五、参考学习一、前言 近期在研究构建寿命预测模型&#xff0c;相信很多数据人都懂建模的过程&#xff0c;其实有80%的时间都是在和数据处…

Docker容器化部署实战:Tomcat与Nginx服务配置指南

部署Tomcat搜索镜像 使用以下命令搜索可用的Tomcat镜像&#xff1a;docker search tomcat拉取镜像 拉取官方Tomcat镜像&#xff1a;docker pull tomcat创建专用目录 为Tomcat配置和数据创建专用目录&#xff1a;mkdir tomcat运行临时容器并复制配置文件 启动临时容器以复制配置…

Go语言实战案例-使用SQLite实现本地存储

在开发工具类软件、桌面应用或者移动端时&#xff0c;我们经常需要一个轻量级数据库来做 本地存储。相比 MySQL、Postgres 等服务型数据库&#xff0c;SQLite 体积小、零配置、单文件存储&#xff0c;非常适合这种场景。Go 语言通过 GORM SQLite 驱动 就能轻松实现。本文将带你…

云计算学习100天-第23天

主机192.168.88.5 安装nginx#安装编译工具&#xff0c;正则表达式依赖包&#xff0c;SSL加密依赖包 yum -y install gcc make pcre-devel openssl-devel tar -xf /root/lnmp_soft.tar.gz cd lnmp_soft/ tar -xf nginx-1.22.1.tar.gz cd nginx-1.22.1/ #指定安装路径&…

【生成树+环】题解:P3907 环的异或_图论_环_异或_搜索_算法竞赛_C++

推销洛谷博客&#xff1a;https://www.luogu.com.cn/article/znmr9iu9 Link&#xff1a;Luogu - P3907 这里默认题目中指的环都是“简单环”&#xff08;即没有“环套环”的情况&#xff09;。 众所周知&#xff0c;树是图的一种特殊情况&#xff0c;且一定无环。如果我们想…

数据库优化提速(二)排序优化之搜索大数据酒店,进销存AI—仙盟创梦IDE

在 MySQL 数据库管理中&#xff0c;排序操作对于数据的有效展示和分析至关重要。本文将以一个实际的 SQL 查询为例&#xff0c;深入探讨排序优化方案&#xff0c;并结合进销存、酒店、知识库等大数据场景&#xff0c;阐述这些优化策略的应用价值。原始SELECT 应用编号, 应用序列…

Linux之Ansible自动化运维(二)

一、ansible Playbook应用由于服务器数量很多&#xff0c;配置信息比较多&#xff0c;因此可以利用Ansible Playbook编写任务自动化与流程化脚本Playbook 由一个或多个play组成的列表&#xff0c;play的主要功能Ansible中Task定义好的角色&#xff0c;指定剧本对应的服务器组二…

ArrayList线程不安全问题及解决方案详解

问题背景在多线程编程中&#xff0c;我们经常会遇到集合类的线程安全问题。Java中的ArrayList是一个常用的集合类&#xff0c;但它不是线程安全的。当多个线程同时操作同一个ArrayList实例时&#xff0c;可能会出现各种不可预料的问题。问题演示List<String> list new A…

车辆方向数据集 - 物体检测

关于数据集 包含超过50,000 张图像中具有方向的车辆的 50,000 多万个注释。它通过同时提供车辆类别和方向来减少对方向进行分类的辅助神经网络的需求。 预训练权重 我们将继续添加在车辆方向数据集和合成车辆方向数据集上训练的各种对象检测模型。如果您需要一些特定的预训练权…

Nextcloud搭建教程:使用Docker在腾讯云服务器上自建私人云盘

更多云服务器知识&#xff0c;尽在hostol.com你那百兆光纤的宽带。你是否也曾看着自己最珍贵的家庭照片、最私密的个人文档&#xff0c;静静地躺在某个科技巨头的服务器上&#xff0c;感到过一丝丝的不安&#xff1f;你的数据&#xff0c;到底被如何“阅读”和“分析”&#xf…

【操作记录】MNN Chat Android App 构建笔记(二)

&#x1f4d2; MNN Chat Android App 构建笔记 一、背景知识MNN 简介 MNN 是阿里开源的轻量级深度学习框架&#xff0c;支持 Android / iOS / Linux / Windows。提供推理、LLM、Vision、Audio 等模块。Android App 里用到的是 Java JNI 调用 MNN 库。CMake NDK 的作用 CMake&…

如何在 Axios 中处理多个 baseURL 而不造成混乱

网罗开发&#xff08;小红书、快手、视频号同名&#xff09;大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等方…

AP服务发现PRS_SOMEIPSD_00255 的解析

[PRS_SOMEIPSD_00255 ] 「SOME/IP-SD头部的重启标志&#xff0c;对于重启后发出的所有报文&#xff0c;都应设置为 1&#xff0c;直至 SOME/IP头部中的会话 ID (Session-ID) 回绕并因此再次从 1 开始。在此回绕之后&#xff0c;重启标志应设置为 0。」(RS_SOMEIPSD_00006)核心含…