DAY64||dijkstra(堆优化版)精讲 ||Bellman_ford 算法精讲

news2024/11/16 15:59:15

dijkstra(堆优化版)精讲

题目如上题47. 参加科学大会(第六期模拟笔试)

邻接表

本题使用邻接表解决问题。

邻接表的优点:

  • 对于稀疏图的存储,只需要存储边,空间利用率高
  • 遍历节点链接情况相对容易

缺点:

  • 检查任意两个节点间是否存在边,效率相对低,需要 O(V)时间,V表示某节点链接其他节点的数量。
  • 实现相对复杂,不易理解

  • 节点1 指向 节点3 权值为 1
  • 节点1 指向 节点5 权值为 2
  • 节点2 指向 节点4 权值为 7
  • 节点2 指向 节点3 权值为 6
  • 节点2 指向 节点5 权值为 3
  • 节点3 指向 节点4 权值为 3
  • 节点5 指向 节点1 权值为 10

这样 我们就把图中权值表示出来了。

但是在代码中 使用 pair<int, int> 很容易让我们搞混了,导致代码可读性很差。

那么 可以 定一个类 EDGE来取代 pair<int, int>

 堆优化三部曲

1.选源点到哪个节点近且该节点未被访问过,我们需要一个 小顶堆 来帮我们对边的权值排序,每次从小顶堆堆顶 取边就是权值最小的边

2.该最近节点被标记访问过,和朴素版一样

3.更新非访问节点到源点的距离

和朴素版区别

  • 邻接表的表示方式不同
  • 使用优先级队列(小顶堆)来对新链接的边排序

代码

堆优化的时间复杂度 只和边的数量有关 和节点数无关,在 优先级队列中 放的也是边。

#include <iostream>
#include <vector>
#include<list>
#include<queue>
#include <climits>
using namespace std;
class mycomparison//小顶堆比较器
{
    public:
    //重载 () 操作符,比较两个 pair<int, int> 的第二个元素,
    //返回 lhs.second > rhs.second,使得优先队列按第二个元素从小到大排序。
    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 v):to(t),val(v){}//构造函数
    
};

int main() {
    int n,m,p1,p2,val;//公共汽车站数量,公路数量,某站到某站及其所花时间
    cin>>n>>m;
    vector<list<Edge>>grid(n+1);//每个元素是一个链表 list<Edge>,用于存储图的邻接表。
    
    //读取并填充邻接表
    for(int i=0;i<m;i++)
    {
        cin>>p1>>p2>>val;
        grid[p1].push_back(Edge(p2,val));//将边 p1 到 p2 的信息添加到 grid[p1] 中。
    }
    
    int start=1;
    int end=n;//初始点和终点
    
    vector<int>minDist(n+1,INT_MAX);//存储从起始点到每个节点的最短距离
    vector<bool>visited(n+1,false);
    minDist[start]=0;//初始点到自身的距离是0
    
    
    //声明一个优先队列 pq,元素类型为 pair<int, int>,使用 mycomparison 作为比较器,实现小顶堆。
    priority_queue<pair<int,int>,vector<pair<int,int>>,mycomparison>pg;
    pg.push(pair<int,int>(start,0));//将起始点及其距离0加入优先队列。
    
    //dijkstra算法核心
    while(!pg.empty())
    {
        // <节点, 源点到该节点的距离>
        // 1、选距离源点最近且未访问过的节点
        pair<int,int>cur=pg.top();pg.pop();
        if(visited[cur.first])continue;
        
        visited[cur.first]=true;//2.标记该节点已被访问
        
        //3.更新非访问节点到源点的距离
        //遍历当前节点 cur.first 指向的所有邻接节点 edge。
        for(Edge edge:grid[cur.first])
        {
            //如果邻接节点 edge.to 未被访问且通过当前节点 cur.first 到达 edge.to 的距离更短,则更新
            if(!visited[edge.to]&&minDist[cur.first]+edge.val<minDist[edge.to])
            {
                minDist[edge.to]=minDist[cur.first]+edge.val;
                
                //将邻接节点 edge.to 及其新的距离加入优先队列。
                pg.push(pair<int,int>(edge.to,minDist[edge.to]));
                
            }
        }
        
    }
    if(minDist[end]==INT_MAX)cout<<-1<<endl;//无法到达目标
    else
    cout<<minDist[end]<<endl;//到达终点的最短路径

}

模拟运行结果

4 4
1 2 1
1 3 4
2 3 1
3 4 1
  • 首先读取 n=4m=4,初始化邻接表 grid

  • 读取边并填充邻接表:

    • 边 (1, 2, 1)grid[1].push_back(Edge(2, 1))
    • 边 (1, 3, 4)grid[1].push_back(Edge(3, 4))
    • 边 (2, 3, 1)grid[2].push_back(Edge(3, 1))
    • 边 (3, 4, 1)grid[3].push_back(Edge(4, 1))
  • 初始化 minDistvisited

  • 初始化优先队列,将起始点及其距离0加入优先队列。

  • 运行 Dijkstra 算法:

    • 第一次迭代:从优先队列中取出节点1,更新 minDist 为 [0, 1, 4, INT_MAX],并将节点2和3加入优先队列。
    • 第二次迭代:从优先队列中取出节点2,更新 minDist 为 [0, 1, 2, INT_MAX],并将节点3加入优先队列。
    • 第三次迭代:从优先队列中取出节点3,更新 minDist 为 [0, 1, 2, 3],并将节点4加入优先队列。
    • 第四次迭代:从优先队列中取出节点4,minDist 保持不变。
  • 3

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

提示信息

示例中最佳路径是从 1 -> 2 -> 5 -> 6,路上的权值分别为 1 2 -2,最终的最低运输成本为 1 + 2 + (-2) = 1。

示例 2:

4 2
1 2 -1
3 4 -1

在此示例中,无法找到一条路径从 1 通往 4,所以此时应该输出 "unconnected"。

数据范围:

1 <= n <= 1000;
1 <= m <= 10000;

-100 <= v <= 100;

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

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

 what is 松弛(核心)

例如一条边,节点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算法 也是采用了动态规划的思想,即:将一个问题分解成多个决策阶段,通过状态之间的递归关系最后计算出全局最优解。

为什么松弛“n-1”次


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

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

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

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

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

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

代码 

#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int main()
{
    int n,m,p1,p2,val;//节点数、边数、边的两个节点和边的权重。
    cin>>n>>m;
    
    vector<vector<int>>grid;
    for(int i=0;i<m;i++)//每条边用一个包含三个整数的向量表示。
    {
        cin>>p1>>p2>>val;
        grid.push_back({p1,p2,val});
    }
    
    int start=1;
    int end=n;
    
    vector<int>minDist(n+1,INT_MAX);
    minDist[start]=0;
    
    //Bellman-Ford 算法核心
    for(int i=1;i<n;i++)//对所有边松弛一次
    {
        for(vector<int>&side:grid)//每次松弛,都是对所有边松弛一次
        {
            int from=side[0];//边的出发点
            int to=side[1];//边的到达点
            int price=side[2];//边的权值
            //松弛操作
            //minDist[from] != INT_MAX 防止从未计算过的节点出发
            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;//到达终点最短路径
    
}

模拟运行结果

假设输入如下:

4 4
1 2 1
1 3 4
2 3 1
3 4 1
  • 首先读取 n=4m=4,初始化边的存储 grid

  • 读取边并存储在 grid 中:

    • 边 (1, 2, 1)grid.push_back({1, 2, 1})
    • 边 (1, 3, 4)grid.push_back({1, 3, 4})
    • 边 (2, 3, 1)grid.push_back({2, 3, 1})
    • 边 (3, 4, 1)grid.push_back({3, 4, 1})
  • 初始化 minDist,设置 minDist[start] = 0

  • 运行 Bellman-Ford 算法:

    • 第一次松弛:
      • 处理边 (1, 2, 1),更新 minDist[2] = 1
      • 处理边 (1, 3, 4),更新 minDist[3] = 4
      • 处理边 (2, 3, 1),更新 minDist[3] = 2
      • 处理边 (3, 4, 1),更新 minDist[4] = 3
    • 第二次松弛:
      • 处理边 (1, 2, 1)minDist[2] 保持不变。
      • 处理边 (1, 3, 4)minDist[3] 保持不变。
      • 处理边 (2, 3, 1)minDist[3] 保持不变。
      • 处理边 (3, 4, 1)minDist[4] 保持不变。
    • 第三次松弛:
      • 处理边 (1, 2, 1)minDist[2] 保持不变。
      • 处理边 (1, 3, 4)minDist[3] 保持不变。
      • 处理边 (2, 3, 1)minDist[3] 保持不变。
      • 处理边 (3, 4, 1)minDist[4] 保持不变。
  • 最终输出:

    3

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

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

相关文章

在openi平台 基于华为顶级深度计算平台 openmind 动手实践

大家可能一直疑问&#xff0c;到底大模型在哪里有用。 本人从事的大模型有几个方向的业务。 基于生成式语言模型的海事航行警告结构化解析。 基于生成式语言模型的航空航行警告结构化解析。 基于生成式生物序列&#xff08;蛋白质、有机物、rna、dna、mrna&#xff09;的多模态…

Figma汉化:提升设计效率,降低沟通成本

在UI设计领域&#xff0c;Figma因其强大的功能而广受欢迎&#xff0c;但全英文界面对于国内设计师来说是一个不小的挑战。幸运的是&#xff0c;通过Figma汉化插件&#xff0c;我们可以克服语言障碍。以下是两种获取和安装Figma汉化插件的方法&#xff0c;旨在帮助国内的UI设计师…

深度学习-卷积神经网络CNN

案例-图像分类 网络结构: 卷积BN激活池化 数据集介绍 CIFAR-10数据集5万张训练图像、1万张测试图像、10个类别、每个类别有6k个图像&#xff0c;图像大小32323。下图列举了10个类&#xff0c;每一类随机展示了10张图片&#xff1a; 特征图计算 在卷积层和池化层结束后, 将特征…

关于adb shell登录开发板后terminal显示不完整

现象 今天有个同事跟我说&#xff0c;adb shell 登录开发板后&#xff0c;终端显示不完整&#xff0c;超出边界后就会出现奇怪的问题&#xff0c;比如字符覆盖显示等。如下图所示。 正常情况下应该如下图所示&#xff1a; 很明显&#xff0c;第一张图的显示区域只有完整区域…

【论文分享】三维景观格局如何影响城市居民的情绪

城市景观对居民情绪的影响是近些年来讨论的热门话题之一&#xff0c;现有的研究主要以遥感影像为数据来源&#xff0c;进行二维图像-数据分析&#xff0c;其量化结果精确度有限。本文引入了三维景观格局的研究模型&#xff0c;通过街景图片及网络发帖信息补充图像及数据来源&am…

ChatGPT学术专用版,一键润色纠错+中英互译+批量翻译PDF

ChatGPT academic项目是由中科院团队基于ChatGPT专属定制。论文润色、语法检查、中英互译、代码解释等可一键搞定&#xff0c;堪称科研神器。 功能介绍 我们以3.5版本为例&#xff0c;ChatGPT学术版总共分为五个区域&#xff1a;输入控制区、输出对话区、基础功能区、函数插件…

Go 语言已立足主流,编程语言排行榜24 年 11 月

Go语言概述 Go语言&#xff0c;简称Golang&#xff0c;是由Google的Robert Griesemer、Rob Pike和Ken Thompson在2007年设计&#xff0c;并于2009年11月正式宣布推出的静态类型、编译型开源编程语言。Go语言以其提高编程效率、软件构建速度和运行时性能的设计目标&#xff0c;…

一、HTML

一、基础概念 1、浏览器相关知识 这五个浏览器市场份额都非常大&#xff0c;且都有自己的内核。 什么是内核&#xff1a; 内核是浏览器的核心&#xff0c;用于处理浏览器所得到的各种资源。 例如&#xff0c;服务器发送图片、视频、音频的资源&#xff0c;浏览…

VRRP HSRP GLBP 三者区别

1. VRRP&#xff08;Virtual Router Redundancy Protocol&#xff0c;虚拟路由冗余协议&#xff09; 标准协议&#xff1a;VRRP 是一种开放标准协议&#xff08;RFC 5798&#xff09;&#xff0c;因此支持的厂商较多&#xff0c;通常用于多种网络设备中。主备模式&#xff1a;…

Elasticsearch:管理和排除 Elasticsearch 内存故障

作者&#xff1a;来自 Elastic Stef Nestor 随着 Elastic Cloud 提供可观察性、安全性和搜索等解决方案&#xff0c;我们将使用 Elastic Cloud 的用户范围从完整的运营团队扩大到包括数据工程师、安全团队和顾问。作为 Elastic 支持代表&#xff0c;我很乐意与各种各样的用户和…

Java集合(Collection+Map)

Java集合&#xff08;CollectionMap&#xff09; 为什么要使用集合&#xff1f;泛型 <>集合框架单列集合CollectionCollection遍历方式List&#xff1a;有序、可重复、有索引ArrayListLinkedListVector&#xff08;已经淘汰&#xff0c;不会再用&#xff09; Set&#xf…

大数据如何助力干部选拔的公正性

随着社会的发展和进步&#xff0c;干部选拔成为组织管理中至关重要的一环。传统的选拔方式可能存在主观性、不公平性以及效率低下等问题。大数据技术的应用&#xff0c;为干部选拔提供了更加全面、精准、客观的信息支持&#xff0c;显著提升选拔工作的科学性和公正性。以下是大…

EHOME视频平台EasyCVR多品牌摄像机视频平台监控视频编码H.265与Smart 265的区别?

在视频监控领域&#xff0c;技术的不断进步推动着行业向更高效、更智能的方向发展。特别是在编码技术方面&#xff0c;Smart 265作为一种新型的视频编码技术&#xff0c;相较于传统的H.265&#xff0c;有明显优势。这种技术的优势在EasyCVR视频监控汇聚管理平台中得到了充分的体…

Docker:查看镜像里的文件

目录 背景步骤1、下载所需要的docker镜像2、创建并运行临时容器3、停止并删除临时容器 背景 在开发过程中&#xff0c;为了更好的理解和开发程序&#xff0c;有时需要确认镜像里的文件是否符合预期&#xff0c;这时就需要查看镜像内容 步骤 1、下载所需要的docker镜像 可以使…

【Vitepress报错】Error: [vitepress] 8 dead link(s) found.

原因 VitePress 在编译时&#xff0c;发现 死链接(dead links) 会构建失败&#xff01;具体在哪我也找不到… 解决方案 如图第一行蓝色提示信息&#xff0c;设置 Vitepress 属性 ignoredeadlinks 为 true 可忽略报错。 .vuepress/config.js export default defineConfig(…

HTB:Squashed[WriteUP]

目录 连接至HTB服务器并启动靶机 使用rustscan对靶机TCP端口进行开放扫描 使用nmap对靶机开放端口进行脚本、服务扫描 使用浏览器访问靶机80端口页面 使用showmount列出靶机上的NFS共享 新建一个test用户 使用Kali自带的PHP_REVERSE_SHELL并复制到一号挂载点 尝试使用c…

数据分析-48-时间序列变点检测之在线实时数据的CPD

文章目录 1 时间序列结构1.1 变化点的定义1.2 结构变化的类型1.2.1 水平变化1.2.2 方差变化1.3 变点检测1.3.1 离线数据检测方法1.3.2 实时数据检测方法2 模拟数据2.1 模拟恒定方差数据2.2 模拟变化方差数据3 实时数据CPD3.1 SDAR学习算法3.2 Changefinder模块3.3 恒定方差CPD3…

ThriveX 博客管理系统前后端项目部署教程

前端 前端项目地址&#xff1a;https://github.com/LiuYuYang01/ThriveX-Blog 控制端项目地址&#xff1a;https://github.com/LiuYuYang01/ThriveX-Admin Vercel 首先以 Vercel 进行部署&#xff0c;两种方式部署都是一样的&#xff0c;我们以前端项目进行演示 首先我们先…

Seata源码笔记(三)

Seata源码笔记&#xff08;三&#xff09; RPC部分基础接口AbstractNettyRemotinginit方法send方法&#xff08;仅看sendSync&#xff09;sendSync中的钩子 AbstractNettyRemotingClient 基于incubator-seata-2.x RPC部分 基础接口 AbstractNettyRemoting init方法 主要设置…

Verilog HDL学习笔记

Verilog HDL&#xff08;Hardware Description Language&#xff09;是在一种硬件描述语言&#xff0c;类似于计算机的高级编程设计语言&#xff0c;它具有灵活性高&#xff0c;容易学习和使用等特点&#xff0c;同时Verilog能够通过文本的形式来描述数字系统的硬件结构和功能。…