代码随想录算法训练营 Day59 图论Ⅸ dijkstra优化版 bellman_ford
图论
题目
47. 参加科学大会(第六期模拟笔试)
改进版本的 dijkstra 算法(堆优化版本)
朴素版本的 dijkstra 算法解法的时间复杂度为 O ( n 2 ) O(n^2) O(n2) 时间复杂度与 n 有关系,与边无关系
类似于 prim 对应点多的情况,kruskal 对应边多的情况
Djkstra 也可以着重于边,着重于边那么存储结构使用邻接表实现
优化的方法是:将边加入小顶堆中实现自动排序(最小边在堆顶),每次从堆顶取边即可
堆实现的三部曲,由于邻接表的存在不需要 for 循环遍历可能的边了,因此有如下代码
1. 第一步,选择原点到节点最近且未访问过的边,pair<节点编号,源点到该节点的权值>
我们不用 for 循环去遍历,直接取堆顶元素:
pair<int, int> cur = pq.Top (); pq.Pop ();
2. 第二步,选择最近节点标记访问过
Visited[cur. First] = true;
3. 第三步,更新非访问节点到原点距离(minDist 数组)
所以在邻接表中,我们要获取节点 cur 链接指向哪些节点,就是遍历 grid[cur 节点编号]
这个链表
cur.first 就是cur节点编号,参考上面pair的定义: pair<节点编号,源点到该节点的权值>
接下来就是更新非访问节点到源点的距离
// 3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)
for (Edge edge : grid[cur.first]) { // 遍历 cur指向的节点,cur指向的节点为 edge// cur指向的节点edge.to,这条边的权值为 edge.valif (!visited[edge.to] && minDist[cur.first] + edge.val < minDist[edge.to]) { // 更新minDistminDist[edge.to] = minDist[cur.first] + edge.val;pq.push(pair<int, int>(edge.to, minDist[edge.to]));}
}
完整代码实现
#include <iostream>
#include <vector>
#include <list>
#include <queue>
#include <climits>using namespace std;// 小顶堆
class MyCmp {
public:bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {return lhs.second > rhs.second;}
};struct Edge {int to; // 邻接顶点int val; // 边权重Edge(int t, int w): to(t), val(w) {} // 构造函数
};int main() {int n, m, x, y, val;cin >> n >> m;// 构造邻接表vector<list<Edge>> grid(n+1);vector<int> minDist(n+1, INT_MAX);vector<bool> vis(n+1, false);for (int i = 0; i < m; ++i) {cin >> x >> y >> val;grid[x].push_back(Edge(y, val));}int start = 1;int end = n;// 构造最小堆 存放pair<节点,节点到该节点的权值>// 参数1 存储元素类型 参数2 容器底层实现 参数3自定义比较函数priority_queue<pair<int, int>, vector<pair<int, int>>, MyCmp> pq;// 队列初始化为0pq.push(pair<int, int>(start, 0));minDist[start] = 0; // 起点距离0while (!pq.empty()) {// 1. 第一步选择原点到节点最近且未被访问过的点pair<int, int> cur = pq.top();pq.pop();if (vis[cur.first]) continue;// 2.标记访问过vis[cur.first] = true;// 3.更新非访问节点到原点距离for (Edge e : grid[cur.first]) {// 获得当前节点指向的节点// cur指向节点e.to 边权值未e.valif (!vis[e.to] && minDist[cur.first] + e.val < minDist[e.to]) {minDist[e.to] = minDist[cur.first] + e.val;pq.push(pair<int, int>(e.to, minDist[e.to]));}}}if (minDist[end] == INT_MAX) cout << -1 << endl;else cout << minDist[end] << endl;
}
94. 城市间货物运输 I
Bellman_ford 算法介绍,本体不同于上面 dijkstra,此时的权重存在负数
Bellman_ford算法的核心思想是对所有边进行松弛n-1次操作(n为节点数量)从而求得目标最短路
不断尝试更新所有的边,直到所有可能的路径都被找到
什么是松弛?举个例子
每条边有起点、终点和边的权值。例如一条边,节点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
这个过程就叫做 “松弛”
以上讲了这么多,其实都是围绕以下这句代码展开:这句代码就是 Bellman_ford算法的核心操作。
if (minDist[B] > minDist[A] + value) minDist[B] = minDist[A] + value
也可以这样写
minDist[B] = min(minDist[A] + value, minDist[B])
其实 Bellman_ford算法也是采用了动态规划的思想,即:将一个问题分解成多个决策阶段,通过状态之间的递归关系最后计算出全局最优解。
那么为什么是 n - 1次松弛呢?这里要给大家模拟一遍 Bellman_ford 的算法才行,接下来我们来看看对所有边松弛 n - 1 次的操作是什么样的。
模拟过程
初始化过程。起点为节点1,起点到起点的距离为0,所以 minDist[1] 初始化为0
其他节点对应的minDist初始化为max,因为我们要求最小距离,那么还没有计算过的节点默认是一个最大数,这样才能更新最小距离。对所有边进行第一次松弛。
接下来我们来松弛一遍所有的边。边:节点5 -> 节点6,权值为-2 ,minDist[5]
还是默认数值max,所以不能基于节点5 去更新节点6
边:节点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)的最短距离呢?
对所有边松弛一次,相当于计算起点到达与起点一条边相连的节点的最短距离。
注意我上面讲的是 对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离,这里 说的是 一条边相连的节点。
与起点(节点1)一条边相邻的节点,到达节点2 最短距离是 1,到达节点3 最短距离是5。
而 节点1 -> 节点2 -> 节点5 -> 节点3 这条路线 是 与起点 三条边相连的路线了。
所以对所有边松弛一次 能得到 与起点 一条边相连的节点最短距离。
那对所有边松弛两次 可以得到与起点 两条边相连的节点的最短距离。
那对所有边松弛三次 可以得到与起点 三条边相连的节点的最短距离,这个时候,我们就能得到到达节点3真正的最短距离,也就是 节点1 -> 节点2 -> 节点5 -> 节点3 这条路线。
那么再回归刚刚的问题,需要对所有边松弛几次才能得到 起点(节点1) 到终点(节点6)的最短距离呢?节点数量为n,那么起点到终点,最多是 n-1 条边相连。
那么无论图是什么样的,边是什么样的顺序,我们对所有边松弛 n-1 次 就一定能得到 起点到达 终点的最短距离。
其实也同时计算出了,起点到达所有节点的最短距离,因为所有节点与起点连接的边数最多也就是 n-1 条边。
#include <iostream>
#include <vector>
#include <list>
#include <climits>using namespace std;int main() {int n, m, x, y, val;cin >> n >> m;// 使用朴素存储图vector<vector<int>> grid;for (int i = 0; i < m; ++i) {cin >> x >> y >> val;grid.push_back({x, y, val});}int start = 1;int end = n;vector<int> minDist(n+1, INT_MAX);minDist[start] = 0;// 对所有边松弛n-1次for (int i = 1; i < n; ++i) {// 每一次松弛是对所有边松弛for (vector<int>& side : grid) {int from = side[0];int to = side[1];int price = side[2];// 松弛操作if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + price) {minDist[to] = minDist[from] + price;}}}if (minDist[end] == INT_MAX) cout << "unconnected" << endl;else cout << minDist[end] << endl;return 0;
}
相关文章:

代码随想录算法训练营 Day59 图论Ⅸ dijkstra优化版 bellman_ford
图论 题目 47. 参加科学大会(第六期模拟笔试) 改进版本的 dijkstra 算法(堆优化版本) 朴素版本的 dijkstra 算法解法的时间复杂度为 O ( n 2 ) O(n^2) O(n2) 时间复杂度与 n 有关系,与边无关系 类似于 prim 对应点多…...
HTML实战:响应式个人资料页面
我将创建一个现代化的响应式个人资料页面,展示HTML在实际应用中的强大功能。这个页面将包含多个实战元素:导航栏、个人简介、技能展示、作品集和联系表单。 设计思路 使用Flexbox和Grid布局实现响应式设计 添加CSS过渡效果增强交互体验 实现深色/浅色模式切换功能 创建悬停动…...
Mac电脑上本地安装 MySQL并配置开启自启完整流程
文章目录 一、mysql安装1.1 使用 Homebrew 安装(推荐)1.2 手动下载 MySQL 社区版1.3 常见问题1.4 图形化管理工具(可选) 二、Mac 上配置 MySQL 开机自动启动2.1 使用 launchd 系统服务(原生支持)2.2 通过 H…...
JavaSE:面向对象进阶之内部类(Inner Class)
JavaSE 面向对象进阶之内部类(Inner Class) 一、内部类的核心概念 内部类是定义在另一个类内部的类,它与外部类存在紧密的逻辑关联,主要作用: 封装细节:隐藏实现细节,对外提供简洁接口。访问…...

【HW系列】—安全设备介绍(开源蜜罐的安装以及使用指南)
文章目录 蜜罐1. 什么是蜜罐?2. 开源蜜罐搭建与使用3. HFish 开源蜜罐详解安装步骤使用指南关闭方法 总结 蜜罐 1. 什么是蜜罐? 蜜罐(Honeypot)是一种主动防御技术,通过模拟存在漏洞的系统或服务(如数据库…...

汽车总线分析总结(CAN、LIN、FlexRay、MOST、车载以太网)
目录 一、汽车总线技术概述 二、主流汽车总线技术对比分析 1. CAN总线(Controller Area Network) 2. LIN总线(Local Interconnect Network) 3. FlexRay总线 4. MOST总线(Media Oriented Systems Transport&#x…...

MyBatisPlus--条件构造器及自定义SQL详解
条件构造器 在前面学习快速入门的时候,练习的增删改查都是基于id去执行的,但是在实际开发业务中,增删改查的条件往往是比较复杂的,因此MyBatisPlus就提供了一个条件构造器来帮助构造复杂的条件。 MyBatisPlus支持各种复杂的wher…...

OVD开放词汇检测 Detic 训练COCO数据集实践
0、引言 纯视觉检测当前研究基本比较饱和,继续创新提升空间很小,除非在CNN和transformer上提出更强基础建模方式。和文本结合是当前的一大趋势,也是计算机视觉和自然语言处理结合的未来趋势,目前和文本结合的目标检测工作还是有很…...

docker、ctr、crictl命令简介与使用
概述 在使用k3s过程中,经常需要使用ctr和crictl两个命令,本文记录一下。 ctr 类似docker命令是docker-shim容器运行时的客户端工具,ctr是Containerd的客户端工具。一个简单的CLI接口,用作Containerd本身的一些调试用途…...
WEB安全--SQL注入--bypass技巧2
继之前文章的补充: WEB安全--SQL注入--bypass技巧_sql注入过滤空格-CSDN博客 Q1:发现sql注入的时间盲注时,如果时间盲注的函数都被过滤了,怎么办? 除了找其他函数替换、编码等方式,还有以下方式绕过&…...
【强化学习哲学 Day 1】Q-Learning - 在不确定中寻找确定
🎭 故事:那些选择的时刻 你还记得那些站在十字路口的时刻吗? 也许是刚进实验室,面对满墙的研究方向海报,不知道哪条路通向你想要的未来;也许是第一份工作的选择,大厂的螺丝钉还是小公司的多面…...

WEB3——什么是ABI
怎么获得ABI? 在编译完合约后,可以在左边下面点击复制ABI ABI(Application Binary Interface,应用二进制接口)是用来让前端或服务端 JavaScript 代码与智能合约进行交互的桥梁,它描述了合约的函数、事件和…...

嵌入式软件--stm32 DAY 8.5 基础复习总结
1.时钟树 在数据手册里面,有一张密密麻麻的图,正是时钟系统里的时钟树。 对于时钟,我们注意有两点。一个是系统时钟SYSCLK,一个是依赖外部晶振生成的RTC. RTC以外部低速晶振作为时钟源或者外部高速晶振128分频后作为时钟源,又或者…...

MMRL: Multi-Modal Representation Learning for Vision-Language Models(多模态表示学习)
摘要 预训练的VLMs,对于跨任务的迁移学习至关重要,然而,在few-shot数据集上微调会导致过拟合,降低在新任务上的性能。为解决这个问题,提出一种新的多模态表征学习框架(MMRL),该框架引入了一个共享、可学习…...
贪心算法求解汽车加油问题
一、问题描述 一辆汽车加满油后可以行驶 n km。在前往目的地的途中,有多个加油站。我们的目标是设计一个有效的算法,确定汽车应该在哪些加油站停靠加油,以使得沿途的加油次数最少。 二、输入输出形式 算法的输入包括两部分:第一…...
JVM Full GC 频繁问题排查、优化及解决方案
引言 在Java应用程序中,JVM(Java虚拟机)通过垃圾回收机制自动管理内存,确保不再使用的对象能够被及时清理和释放。虽然垃圾回收在大多数情况下运行顺利,但当Full GC频繁发生时,它会严重影响应用性能&#x…...

rsync服务的搭建
目录 一、rsync介绍 rsync的安装 二、rsync的语法 三、rsync命令使用 1. 本机同步 2. 远程同步 四、rsync作为服务使用 1、尝试启动rsync程序 2、rsync的配置文件介绍 注意事项: 3. rsyncinotify实时同步 3.依赖服务托管xinetd(CentOS 6中rs…...
JDK21深度解密 Day 8:Spring Boot 3与虚拟线程整合
【JDK21深度解密 Day 8】Spring Boot 3与虚拟线程整合 引言:Spring Boot 3遇上JDK21虚拟线程 在本系列的第8天,我们将聚焦于Spring Boot 3与JDK21虚拟线程的整合实践。作为全网首套完整的JDK21特性解析,我们不仅会探讨虚拟线程如何颠覆传统Java并发模型,还会通过完整的Sp…...

vscode 配置 QtCreat Cmake项目
1.vscode安装CmakeTool插件并配置QT中cmake的路径,不止这一处 2.cmake生成器使用Ninja(Ninja在安装QT时需要勾选),可以解决[build] cc1plus.exe: error: too many filenames given; type ‘cc1plus.exe --help’ for usage 编译时…...
排序算法-归并排序与快速排序
归并排序与快速排序 快速排序是利用的递归思想:选取一个基准数,把小于基准数的放左边 大于的放右边直到整个序列有序 。快排分割函数 O(lognn), 空间 :没有额外开辟新的数组但是递归树调用函数会占用栈内存 O(logn) 。 归并排序:在递归返回的…...

HTML实现端午节主题网站:龙舟争渡,凭吊祭江诵君赋。
名人说:龙舟争渡,助威呐喊,凭吊祭江诵君赋。——苏轼《六幺令天中节》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、项目概览:传统与现代的技术碰撞1. 核心特…...

uniapp uni-id 如果是正式项目,需自行实现发送邮件的相关功能
(3) 使用云对象sendEmailCode 发送邮箱验证码,报错送邮箱验证码失败 Error: 已启动测试模式,直接使用:123456作为邮箱验证码即可。 如果是正式项目,需自行实现发送邮件的相关功能 - DCloud问答 uni-id 没有实现邮箱验证码逻辑&am…...
Spring boot 策略模式
public abstract class Node {/*** 执行** param a* param b* return*/public abstract Integer execute(int a, int b); }package my.node;import org.springframework.stereotype.Component;Component("exec") public class ExecNode extends Node {Overridepublic…...
websocket在vue中的使用步骤,以及实现聊天
一、WebSocket集成步骤 连接初始化 在Vue组件中创建WebSocket实例,建议在mounted生命周期中执行: data() {return {socket: null,messages: []} }, mounted() {this.socket new WebSocket(wss://your-server-endpoint); }事件监听配置 连接成…...

C++学习-入门到精通【12】文件处理
C学习-入门到精通【12】文件处理 目录 C学习-入门到精通【12】文件处理一、文件和流二、创建顺序文件三、从顺序文件读取数据文件定位指针对之前的程序进行修改:贷款查询程序 四、更新顺序文件五、随机存取文件1.创建随机存取文件2.修改程序:贷款处理程序…...
第十一篇:MySQL 在分布式系统中的一致性保障与中间件实践
随着微服务和分布式架构的发展,单点数据库早已无法满足系统的横向扩展需求。本篇聚焦 MySQL 在分布式系统中的一致性保障机制,以及相关中间件的使用策略与实战经验。 一、一致性问题的由来 在 单机 MySQL 环境 中,事务具有原子性、隔离性&am…...
Java中如何枚举正则表达式捕获组的名字
在使用正则表达式在匹配文本时,除了可以通过表达式捕获命中的文本串外,还可以对捕获的文本串进行命名。尤其是在解析日志的场景中,经常会被用到。表达式如下: \<(?<pri>\d)\>(?<time>.*) (?<host>\S)…...
matlab实现图像压缩编码
一、基于DCT的JPEG压缩(有损) 1. 核心步骤 图像分块:将图像划分为88的小块。离散余弦变换(DCT):对每个块进行DCT变换。量化:对DCT系数进行量化以减少高频信息。熵编码:使用哈夫曼或…...
如何排查Redis单个Key命中率骤降?
问题现象 Redis整体命中率98%,但监控发现特定Key(如user:1000:profile)的命中率从99%骤降至40%,引发服务延迟上升。 排查步骤 1. 确认现象与定位Key // 通过Redis监控工具获取Key指标 public void monitorKey(String key) {Je…...

记一次 Starrocks be 内存异常宕机
突发性 be 内存飙高,直至被系统 kill 掉,be 内存如下:其中 starrocks_be_update_mem_bytes 指标打满,重启也是如此 [rootlocalhost bin]# curl -XGET -s http://192.168.1.49:8040/metrics | grep "^starrocks_be_.*_mem_b…...