Dijkstra求最短路 II(堆优化Dijkstra算法)

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。

请你求出 11 号点到 n 号点的最短距离,如果无法从 11 号点走到 n 号点,则输出 −1−1。

输入格式

第一行包含整数 n 和 m。

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

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 −1−1。

数据范围

1≤n,m≤1.5×10^5,
图中涉及边长均不小于 0,且不超过 10000。
数据保证:如果最短路存在,则最短路的长度不超过 10^9。

输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3

思路:这道题的堆优化我们使用优先队列来做(小根堆),而且因为我们既要存当前点到起点的距离,又要存当前点的编号,所以我们使用一个pair,又因为我们是根据距离来判断入堆的时机的(每次离起点最近的点入堆),所以优先排序的是距离,也就是pair的first元素应该是距离。

    typedef pair<int,int> PII;

    priority_queue<PII,vector<PII>,greater<PII>> heap; // 数据类型, 容器, 排序方式
    //定义一个升序的优先队列(小根堆)
    
    heap.push({0,1});  //放入距离,节点编号,这里1号点到1号点的距离是0 
   //这个顺序不能倒,pair排序时是先根据first,再根据second,这里显然要根据距离排序
    

先看完整代码 

示例代码:
#include<iostream>
#include<cstring>
#include<algorithm> 
#include<queue>

using namespace std;
const int N=1e6+10;
typedef pair<int,int> PII;
int n,m;
int h[N],e[N],ne[N],w[N],idx; //稀疏图用邻接表存,w存边权值
int dist[N];  //1号点到n号点的最短距离
int st[N];  //当前点是否找到了最短距离

void add(int a,int b,int c) //添加一条从a指向b的边
{
    e[idx]=b;  //算法保证了会选出最短的路,所以不对重边或自环情况做处理,也就是会存在冗余
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx;
    idx++;
}

int dijkstra() //输出一个整数,表示1号点到n号点的最短距离。
{
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    
    priority_queue<PII,vector<PII>,greater<PII>> heap; // 数据类型, 容器, 排序方式
    //定义一个升序的优先队列(小根堆)
    
    heap.push({0,1});  //放入距离,节点编号,这里1号点到1号点的距离是0 
   //这个顺序不能倒,pair排序时是先根据first,再根据second,这里显然要根据距离排序
    
    while(heap.size())
    {
        auto t=heap.top(); //每次找到堆里面最小距离的点,也就是不在集合S中距离最短的点
        heap.pop();
        
        int ver=t.second,distance=t.first; //ver是节点编号,distance是点的距离
        
        if(st[ver]) continue; //如果ver这个点之前出来过,说明当前这个点是冗余(重边自环之类的),也就没有必要继续处理,直接continue,不再执行下面的语句,而是执行下一次的while循环
        
        /*
        堆优化版的是将距离直接加入到堆中.
        例如:dist[5]=9(在堆中{9,5},第一次更新时加入),dist[5]=7(在堆中{7,5},第二次更新时加入)
        使用时用的是dist[5]=7,将该点({7,5})弹出后,在下一次循环中,如果{9,5}在堆顶的话,使用时
        两者间肯定要选距离要小的那个,不能使用{9,5}重复更新,所以要用st数组进行标记
        */
        
        st[ver]=true; //如果这个点之前没有出来过,就标记找到了这个点的最短距离
        
        for(int i=h[ver];i!=-1;i=ne[i])  //每次都取这个距离最小的点来更新其他点,i是当前点ver指向的点的下标(与当前点与连线的点)
        {
            //更新ver指向的节点距离
            //每次只更新最小点的邻接边不是整个图
            
            int j=e[i];  //这里j是下标i对应的节点编号(i存的是下标,下标对应的点的值就是结点编号)
            if(dist[j]>dist[ver]+w[i])  //如果1到j的最短距离比1到ver的最短距离加上ver到j的距离大
            {
                dist[j]=dist[ver]+w[i]; //那么就更新1到j的最短距离,再把这个新的最短距离加入堆中
                heap.push({dist[j],j}); 
            }
        }
    }
    if(dist[n]==0x3f3f3f3f)
    {
        return -1;
    }
    return dist[n];
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    memset(h,-1,sizeof(h));
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    cout<<dijkstra()<<endl;
    return 0;
}

//一个点到另一个点的距离有10,后来又加了15或者其他的更大距离,但是因为是小根堆,所以最短距离肯定会出现在最前面
//所以除了在堆顶的元素,底下那些元素都算是冗余备份元素,可以舍弃掉了,直接continue就行。

代码基本都比较好理解,注意这道题用邻接表存,其中e[ ]存的就是节点编号

 for(int i=h[ver];i!=-1;i=ne[i])  //每次都取这个距离最小的点来更新其他点,i是当前点ver指向的点的下标(与当前点与连线的点)
        {
            //更新ver指向的节点距离
            //每次只更新最小点的邻接边不是整个图
            
            int j=e[i];  //这里j是下标i对应的节点编号(i存的是下标,下标对应的点的值就是结点编号)
            if(dist[j]>dist[ver]+w[i])  //如果1到j的最短距离比1到ver的最短距离加上ver到j的距离大
            {
                dist[j]=dist[ver]+w[i]; //那么就更新1到j的最短距离,再把这个新的最短距离加入堆中
                heap.push({dist[j],j}); 
            }
        }
关于w[i]和i,j在这里面都代表什么:

 while(heap.size())
    {
        auto t=heap.top(); //每次找到堆里面最小距离的点,也就是不在集合S中距离最短的点
        heap.pop();
        
        int ver=t.second,distance=t.first; //ver是节点编号,distance是点的距离
        
        if(st[ver]) continue; //如果ver这个点之前出来过,说明当前这个点是冗余(重边自环之类的),也就没有必要继续处理,直接continue,不再执行下面的语句,而是执行下一次的while循环
        
        /*
        堆优化版的是将距离直接加入到堆中.
        例如:dist[5]=9(在堆中{9,5},第一次更新时加入),dist[5]=7(在堆中{7,5},第二次更新时加入)
        使用时用的是dist[5]=7,将该点({7,5})弹出后,在下一次循环中,如果{9,5}在堆顶的话,使用时
        两者间肯定要选距离要小的那个,不能使用{9,5}重复更新,所以要用st数组进行标记
        */
        
        st[ver]=true; //如果这个点之前没有出来过,就标记找到了这个点的最短距离
        
        for(int i=h[ver];i!=-1;i=ne[i])  //每次都取这个距离最小的点来更新其他点,i是当前点ver指向的点的下标(与当前点与连线的点)
        {
            //更新ver指向的节点距离
            //每次只更新最小点的邻接边不是整个图
            
            int j=e[i];  //这里j是下标i对应的节点编号(i存的是下标,下标对应的点的值就是结点编号)
            if(dist[j]>dist[ver]+w[i])  //如果1到j的最短距离比1到ver的最短距离加上ver到j的距离大
            {
                dist[j]=dist[ver]+w[i]; //那么就更新1到j的最短距离,再把这个新的最短距离加入堆中
                heap.push({dist[j],j}); 
            }
        }
    }
为什么要有continue和冗余?

代码中的for循环会把所有满足dist[j] > distance + w[i]的点都加进去,有的点它既是a的邻接点又是b的邻接点,这样的点可能会在将a的邻接点更新的时候加进去一次,又在更新b的邻接点的时候又加进去一次,这样队列中就有两个一样的点,虽然距离不同,此时这两个一样的点拓展的点也是一样的,所以我们取距离短的就好。

最后:

连线很多的时候,对应的就是稠密图,显然易见,稠密图的路径太多了,所以就用点来找,也就是抓重点;
点很多,但是连线不是很多的时候,对应的就是稀疏图,稀疏图的路径不多,所以按照连接路径找最短路,这个过程运用优先队列,能确保每一次查找保留到更新到队列里的都是最小的,同时还解决了两个点多条路选择最短路的问题;

参考:AcWing 850. Dijkstra求最短路 II - AcWing

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鱼骨探矿的题解

原题描述&#xff1a; 题目描述&#xff1a; 众所周知&#xff0c;《我的世界》中一种非常流行的探矿方式就是鱼骨探矿。 我的世界的地图可以看作一个的正方形世界。 经过初步探测&#xff0c;在第 i 行&#xff0c;[li, ri] 区间内可能存在宝藏。为了探索效率&#xff0c;…

C# | 对比不同种类的锁

文章目录 C# 对比不同种类的锁异同点对比表使用方法lock语句Monitor类Mutex类Semaphore类ReaderWriterLock类 结语 C# 对比不同种类的锁 Hi&#xff0c;在C#编程中&#xff0c;想要保护共享资源&#xff0c;通常会用到各种类型的锁。今天我们就来一起看看C#中不同种类的锁&…

geolife 笔记:将所有轨迹放入一个DataFrame

单条轨迹的处理&#xff1a;geolife笔记&#xff1a;整理处理单条轨迹-CSDN博客 1 加载数据 import pandas as pd import numpy as np import datetime as dt import osdata_dir Geolife Trajectories 1.3/Data/ 1.1 列出所有文件夹 dirlist os.listdir(data_dir) dirlist…

基于Qt开发的闹钟

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);speecher new QTextToSpeech(this); }Widget::~Widget() {delete ui; }//定时器超时时&#xff0c;自动执行的…

Ubuntu22.04中用户的全名

概要&#xff1a; 用户的全名有别于用户名username username可以理解为账户名&#xff0c;或者说用户ID&#xff0c;用于确定身份&#xff0c;显然是必需的 用户全名则不是必需的&#xff0c;用户全名也叫做注释&#xff0c;是一种辅助信息&#xff0c;如果没有填写用户全名…

docker 资源控制

Docker的资源控制 对容器使用宿主机的资源进行限制&#xff0c;如cpu&#xff0c;内存&#xff0c;磁盘I/O Docker使用linux自带的功能cgroup(control grouos)是linux内核系统提供的一种可以限制&#xff0c;记录&#xff0c;隔离进程组使用的物理资源 Docker借助这个机制&…

MySQL执行流程_执行一条select语句,期间发生了什么

文章目录 执行一条select语句&#xff0c;期间发生了什么MySQL执行流程第一步&#xff1a;连接器第二步&#xff1a;查询缓存第三步&#xff1a;解析SQL第四步&#xff1a;执行SQL 执行一条select语句&#xff0c;期间发生了什么 MySQL执行流程 server层负责建立连接、分析和执…

Banana Pi BPI-R4 SBC/路由器推出,带双 10G SFP+ 端口+Wifi7支持

Banana Pi BPI-R4 wifi7路由器开发板 香蕉派 Banana Pi BPI-R4 根据著名Banana Pi品牌背后的公司Sinovoip提供的初步信息&#xff0c;他们即将推出的Banana Pi BPI-R4路由器板目前已经正式发售。与之前的 Banana Pi R3 板相比&#xff0c;这在规格上将有显着提升。这就是我们…

99基于matlab的小波分解和小波能量熵函数

基于matlab的小波分解和小波能量熵函数&#xff0c;通过GUI界面导入西储大学轴承故障数据&#xff0c;以可视化的图对结果进行展现。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 99小波分解和小波能量熵函数 (xiaohongshu.com)https://www.xiaohongshu.co…

【离散数学】——期末刷题题库( 二元关系)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

eclipse的日志文件放在什么位置

eclipse的日志文件放在<workspace的目录>/.metadata目录下面&#xff0c;例如&#xff1a;

Java基础语法之访问修饰限定符

private 表示私有的&#xff0c;只能在同一个包中的同一个类使用 像这样就是在同一个包中的不同类用了private修饰的变量&#xff0c;这是非法的&#xff0c;那到底该如何给a赋值呢&#xff1f;可以在定义时就赋值&#xff0c;但这样的代码就没有可操作性&#xff0c;所以我们…

Nginx的location匹配和rewrite重写

一、location匹配 常用的正则表达式 ^ &#xff1a;匹配输入字符串的起始位置 $ &#xff1a;匹配输入字符串的结束位置 * &#xff1a;匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll”&#xff1a;匹配前面的字符一次或多次。如“ol”能匹配“ol”及“oll…

java--HashMap、LinkedHashMap、TreeMap底层原理

1.HashMap集合的底层原理 ①HashMap跟HashSet的底层原理是一模一样的&#xff0c;都是基于哈希表实现的。 ②实际上&#xff1a;原来学的Set系列集合的底层原理就是基于Map实现的&#xff0c;只是Set集合中的元素只要键数据&#xff0c;不要值数据而已。 2.哈希表 ①JDK8之前…

原创度检测,在线文章原创度检测

原创度检测&#xff0c;作为数字时代中内容创作者和学术界广泛关注的话题&#xff0c;正逐渐成为保障知识产权、促进创新发展的不可或缺的工具。今天&#xff0c;我们将深入介绍原创度检测的定义、意义、技术原理、应用领域以及未来趋势。 一、什么是原创度检测&#xff1f; 原…

社区分享|宋月冉:大数据下的联邦学习隐私安全问题

“隐语”是开源的可信隐私计算框架&#xff0c;内置 MPC、TEE、同态等多种密态计算虚拟设备供灵活选择&#xff0c;提供丰富的联邦学习算法和差分隐私机制 开源项目 github.com/secretflow gitee.com/secretflow 本文根据隐语开源社区 Contributor 西安电子科技大学网络与信息…

Gemini与GPT-4的巅峰对决:AI界的双壁之战

随着人工智能技术的飞速发展&#xff0c;AI领域的竞争越来越激烈。在这个充满挑战与机遇的时代&#xff0c;两个备受瞩目的AI巨头——Gemini Pro和GPT-4&#xff0c;成为了人们关注的焦点。这两者都以其强大的功能和卓越的性能&#xff0c;引领着AI领域的发展潮流。本文将详细介…

【Android】完美解决Cannot resolve method ‘subscribe(Observer<T>)‘

问题截图&#xff1a; 解决方法&#xff1a; 如上图&#xff0c;看我标123的三个地方&#xff0c;2标注的地方提示我们我方法实际返回的值是Observer<Res_GetCellCode>,而我想要返回的结果是&#xff1a;3标记的结果&#xff1a;Observer<Res_QueryCTInfo>&#xf…

做为一个产品经理带你了解Axure元件

1. Axure元件简介 2.基本元件 2.1 矩形 2.2 图片 2.3 占位符 2.4 按钮 2.5 标题 ​编辑 2.6 水平线&#xff0c;垂直线 2.7 热区 3.表单元件及表格元件简介 3.1 表单元件简介 3.2 表格元件简介 4.表单案例 4.1 登录界面的制作 4.2 个人简介的制作 1. Axure元件简…

简单自动弃流装置工作原理

电动弃流装置 规格分为&#xff1a;直通式&#xff08;不锈钢外壳&#xff09;、三通式&#xff08;不锈钢外壳&#xff09;、井座式&#xff08;PE外壳&#xff09; 1、直通式规格型号&#xff1a;LLQLKZ-200、LLQLKZ-300、LLQLKZ-400 2、三通式规格型号&#xff1a;LLQLK-…