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

news2024/11/14 12:14:11

给定一个 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

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

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

相关文章

Vue指令之v-else与v-else-if

在上一篇博客中介绍了v-if&#xff0c;而在各式各样的程序语句中 if 和 else 通常是伴生的&#xff0c;在Vue中也不例外&#xff0c;Vue同样提供了v-else和v-else-if指令&#xff0c;其功能就是补充v-if的逻辑判断。 例如&#xff0c;当我们要根据一个分数输出对应的等级&…

路由器的转换原理--ENSP实验

目录 一、路由器的工作原理 二、路由表的形成 1、直连路由 2、非直连路由 2.1静态路由 2.2动态路由 三、静态路由和默认路由 1、静态路由 1.1静态路由的缺点 1.2路由的配置--结合ensp实验 2、默认路由--特殊的静态路由 2.1概念 2.2格式 2.3默认路由的配置--ens…

3GPP标准查看、下载和几个UE相关系列标准

由于一直做终端侧协议。最近以UE为核心重新下载了一系列文档。 总结并举例一下分类标准。 如何查看3GPP标准列表 实际上在3GPP网站如下链接&#xff1a;Specifications by Series&#xff0c;每个系列以及分类都说的很清楚。 几个系列分类举例 和终端协议层工作比较关系密切…

【Java】构建表达式二叉树和表达式二叉树求值

问题背景 1. 实现一个简单的计算器。通过键盘输入一个包含圆括号、加减乘除等符号组成的算术表达式字符串&#xff0c;输出该算术表达式的值。要求&#xff1a; &#xff08;1&#xff09;系统至少能实现加、减、乘、除等运算&#xff1b; &#xff08;2&#xff09;利用二叉…

LeetCode(55)环形链表【链表】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 环形链表 1.题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评…

啊?150水冷踏板卷到7千多,巧格的钱购买150了?

力帆的车一般我是不太想写的&#xff0c;但是顶不住它这个价格&#xff0c;实在是....&#xff0c;标准版售价干到了7980元&#xff0c;和巧格一个价了&#xff0c;比福喜还便宜点&#xff0c;属实是离离原上谱&#xff0c;不过这个车不太影响的了豪爵大哥的UHR&#xff0c;两台…

Cypress安装与使用教程(2)—— 软测大玩家

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

pip list 报错 ImportError: cannot import name ‘main‘ from ‘pip._int

文章目录 报错信息问题原因解决方案 关注公众号&#xff1a;『AI学习星球』 算法学习、4对1辅导、论文辅导或核心期刊可以通过公众号或CSDN滴滴我 报错信息 最近在操作服务器的时候&#xff0c;发现pip list这个命令不好使了&#xff0c;报错如下 外链图片转存失败,源站可能…

[Linux] yum安装分布式LNMP架构

1. 在一台主机安装nginx&#xff08;192.168.136.120&#xff09; 1.1 搭建nginx相关的yum源 cd /yum.repos.d mkdir bak mv *.repo bak vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/7/$basearch/ gpgche…

Self-Distillation from the Last Mini-Batch for Consistency Regularization中文版

Self-Distillation from the Last Mini-Batch for Consistency Regularization 从上一个小批量自发蒸馏&#xff0c;实现一致性正则化 摘要 知识蒸馏&#xff08;Knowledge distillation&#xff0c;KD&#xff09;展示了强大的潜力&#xff0c;作为一种强有力的正则化策略&a…

CETN01 - How to Use Cloud Classroom

文章目录 I. Introduction to Cloud ClassroomII. How to Use Cloud Classroom1. Publish Resources2. Conduct Activities3. Class Teaching Reports4. View Experience Values5. Performance in Cloud Classroom I. 云课堂介绍II. 如何使用云课堂1. 发布资源2. 进行活动3. 班…

C++STL之List的实现

首先我们要实现List的STL,我们首先要学会双向带头链表的数据结构。那么第一步肯定是要构建我们的节点的数据结构。 首先要有数据域&#xff0c;前后指针域即可。 再通过模板类进行模板化。 然后再写List的构造函数&#xff0c;这个地方用T&,通过引用就可以减少一次形参拷…

Android 蓝牙BluetoothAdapter 相关(一)

Android 蓝牙相关 本文主要讲述android 蓝牙的简单使用. 1: 是否支持蓝牙 /*** 是否支持蓝牙** return*/ private boolean isSupportBluetooth() {BluetoothAdapter bluetoothAdapter BluetoothAdapter.getDefaultAdapter();return bluetoothAdapter ! null; }2: 开启蓝牙 …

强大的音频编辑器 Metadatics直装 for mac

Metadatics是一款Mac上的音频元数据编辑器&#xff0c;功能强大且高级。它支持批量编辑最常见的音频文件类型&#xff0c;包括MP3、M4A、AIFF、WAV、FLAC、APE、OGG、WMA等。它可以从在线资源中查找元数据&#xff0c;根据元数据重命名文件&#xff0c;或使用众多内置函数之一来…

Mysql、Oracle安全项检查表及操作脚本

软件开发全资料获取&#xff1a;点我获取 Mysql检查表 Oracle检查表

【Canvas】记录一次从0到1绘制风场空间分布图的过程

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热衷分享有趣实用的文章&#xff0c;希望大家多多支持&#xff0c;一起进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 背景 前置知识 风场数据 绘制风场 准备工作 生成二维网格 获取…

ppt转换成pdf文件

最近用到了&#xff0c;记一下&#xff1b; ppt转pdf分为两种情况: 小于2007版本的 .ppt格式&#xff08;2003&#xff09; 与大于2007版本的 .pptx格式&#xff08;2007&#xff09; .ppt格式为 二进制文件 .pptx格式为xml格式&#xff0c;在java中有不同的jar包需要使用 引入…

MacOS 12 开放指定端口 指定ip访问

MacOS 12 开放指定端口 指定ip访问 在 macOS 上开放一个端口&#xff0c;并指定只能特定的 IP 访问&#xff0c;你可以使用 macOS 内置的 pfctl&#xff08;Packet Filter&#xff09;工具来实现。 以下是一些基本的步骤&#xff1a; 1、 编辑 pf 配置文件&#xff1a; 打开 /…

Dockerfile创建镜像--LNMP+wordpress

实验准备&#xff1a; nginx&#xff1a;172.111.0.10 docker-nginx mysql&#xff1a;172.111.0.20 docker-mysql php&#xff1a;172.111.0.30 docker-php 自定义网段&#xff1a;172.111.0.0/16mkdir nginx mysql php mv nginx-1.22.0.tar.gz wordpress-6.4.2-zh_CN.ta…

数据结构之选择排序

目录 直接选择排序 选择排序的时间复杂度 堆排序 向上调整算法 向下调整算法 向上调整算法建立堆 向下调整算法建立堆 堆排序整体代码 堆排序的时间复杂度 直接选择排序 在之前讲插入排序时&#xff0c;我们讲了这样的一个应用场景&#xff0c;我们在斗地主摸牌时&…