迪杰斯特拉算法——求最短路径

news2024/9/21 18:57:00

朴素版本(时间复杂度O(n^2)): 

迪杰斯特拉算法采用的是一种贪心的策略。  

用一个 dist 数组保存源点到其余各个节点的距离,dist[i] 表示源点到节点 i 的距离。初始时,dist 数组的各个元素为无穷大。 源点到源点的距离为 0。即dist[1] = 0。

用一个状态数组 st记录是否找到了源点到该节点的最短距离,st[i] 如果为真,则表示找到了源点到节点 i 的最短距离,st[i] 如果为假,则表示源点到节点 i 的最短距离还没有找到。初始时,st各个元素为假。

遍历 dist 数组,找到一个节点t,这个节点是:没有确定最短路径的节点中距离源点最近的点。假设该节点编号为 t。此时就找到了源点到该节点的最短距离,st[t] 置为 1。 

遍历 t所有可以到达的节点 j,如果 dist[j] 大于 dist[t] 加上 t -> j 的距离,即 dist[j] > dist[t] + w[t][j](w[t][j] 为 i -> j 的距离) ,则更新 dist[j] = dist[t] + w[t][j]。

样题: 

#include <bits/stdc++.h>
using namespace std;
const int N = 510;
const int INF = INT_MAX; // 使用INT_MAX作为无穷大
int n, m;
int g[N][N], dist[N];
bool st[N];

int dijkstra() {
    memset(dist, 0x3f, sizeof dist); // 初始化距离为无穷大
    dist[1] = 0; // 起点距离设为0
    for (int i = 0; i < n; i++) {
        int t = -1;
        // 找到没有确定最短路径的且距离最小的节点
        for (int j = 1; j <= n; j++) {
            if (!st[j] && (t == -1 || dist[t] > dist[j])) {
                t = j;
            }
        }
        if (t == -1) break; // 如果没有找到有效节点,退出循环
        st[t] = true;

        for (int j = 1; j <= n; j++) {
            if (g[t][j] < INF) {
                dist[j] = min(dist[j], dist[t] + g[t][j]);
            }
        }
    }
    if (dist[n] == INF) return -1;
    return dist[n];
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    memset(g, 0x3f, sizeof g); // 初始化图
    while (m--) {
        int a, b, c; 
        cin >> a >> b >> c;
        g[a][b] = min(g[a][b], c); // 处理多条边,只保留最小权值
    }
    int t = dijkstra();
    cout << t << '\n';
    return 0;
}

扩展 :

题目的意思是,你作为一个城市的紧急救援队队长,当你接到来自另一个城市的紧急求救电话时,你的任务是尽快带领你的队伍前往该城市,并且在前往的途中尽可能多地召集其他城市的救援队伍来支援。

具体来说,题目给出了一张国家的地图,这张地图标明了若干个城市以及它们之间的道路。每个城市有一定数量的救援队伍,每条道路有其长度。你需要计算:

  1. 从起始城市到达目标城市的不同最短路径的数量
  2. 在这些最短路径上能够召集的最大救援队伍数量

输入格式:

输入包括以下内容:

  • 第一行包含4个正整数:N 表示城市的数量(城市编号从0到N-1),M 表示道路的数量,C1 表示你所在的起始城市,C2 表示需要救援的目标城市。
  • 第二行包含 N 个整数,其中第 i 个整数表示城市 i 的救援队伍数量。
  • 接下来的 M 行每行包含3个整数:c1c2L,表示城市 c1 和城市 c2 之间有一条长度为 L 的道路。

输出格式:

输出包括一行,包含两个整数:

  • 从起始城市 C1 到目标城市 C2不同最短路径的数量
  • 在这些最短路径上能召集到的最大救援队伍数量

扩展:不仅要计算最短路径,还要统计路径数量以及路径上的资源最大化问题。

#include <bits/stdc++.h>
using namespace std;

const int N = 510; // 最大城市数量
const int INF = 0x3f3f3f3f; // 无穷大,表示不可达的距离

int n, m, c1, c2; // 城市数量,道路数量,起点城市,终点城市
int g[N][N], dist[N], rescue[N], num_paths[N], max_rescue[N];
bool st[N]; // 标记每个城市是否已确定最短路径

// 迪杰斯特拉算法实现
void dijkstra() {
    // 初始化
    memset(dist, 0x3f, sizeof dist); // 最短距离初始化为无穷大
    memset(num_paths, 0, sizeof num_paths); // 最短路径数量初始化为0
    memset(max_rescue, 0, sizeof max_rescue); // 最大救援团队数初始化为0

    dist[c1] = 0; // 起点到起点的距离为0
    num_paths[c1] = 1; // 起点到起点的路径数量为1
    max_rescue[c1] = rescue[c1]; // 起点的最大救援团队数为自身的救援团队数

    for (int i = 0; i < n; i++) {
        int t = -1; // t代表当前未确定最短路径的城市
        for (int j = 0; j < n; j++) {
            if (!st[j] && (t == -1 || dist[t] > dist[j])) {
                t = j; // 找到距离最小且未确定的城市
            }
        }

        if (t == -1) break; // 如果没有可选的城市,退出循环

        st[t] = true; // 标记城市t的最短路径已确定

        // 更新城市t的所有邻接城市的距离
        for (int j = 0; j < n; j++) {
            if (g[t][j] != INF) { // 如果t和j之间有道路
                // 发现更短路径
                if (dist[j] > dist[t] + g[t][j]) {
                    dist[j] = dist[t] + g[t][j]; // 更新最短距离
                    num_paths[j] = num_paths[t]; // 更新路径数量
                    max_rescue[j] = max_rescue[t] + rescue[j]; // 更新最大救援团队数
                } 
                // 如果发现相同长度的路径
                else if (dist[j] == dist[t] + g[t][j]) {
                    num_paths[j] += num_paths[t]; // 增加该路径的数量
                    max_rescue[j] = max(max_rescue[j], max_rescue[t] + rescue[j]); // 更新最大救援团队数
                }
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m >> c1 >> c2; // 输入城市数量,道路数量,起点和终点
    memset(g, 0x3f, sizeof g); // 初始化图的邻接矩阵为无穷大,表示没有直接连接的道路
    
    // 输入每个城市的救援团队数量
    for (int i = 0; i < n; i++) {
        cin >> rescue[i];
    }

    // 输入每条道路的信息
    while (m--) {
        int a, b, l;
        cin >> a >> b >> l;
        g[a][b] = g[b][a] = l; // 设置无向图中两城市间的距离
    }

    dijkstra(); // 调用迪杰斯特拉算法

    // 输出从c1到c2的最短路径数量和最大救援团队数
    cout << num_paths[c2] << " " << max_rescue[c2] << '\n';

    return 0;
}

 堆优化版:

小根堆(优先队列)的使用

  • 小根堆(Min-Heap) 作为优先队列,用于维护当前待处理的最短路径节点。在迪杰斯特拉算法中,堆始终确保提取出距离起点最近的未处理节点。
  • 这种方式避免了每次都遍历所有节点来找出当前的最短路径节点(这会增加时间复杂度)。通过堆,我们可以在 O(log⁡N)的时间内完成插入和提取操作,使得每个节点的处理都更加高效。

使用优先队列优化的迪杰斯特拉算法的时间复杂度为 O((E+V)log⁡V),其中 V是节点数,E是边数。相比于未优化的版本(时间复杂度 O(V^2),在稀疏图中效率提升明显。

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
/*
第一个值 (first):表示从起点到当前节点的最短距离。
在迪杰斯特拉算法中,堆会根据这个值进行排序,优先处理距离最短的节点。

第二个值 (second):表示当前的节点编号。
这个编号用于标识从堆中取出的节点,并用来更新该节点的邻接节点的最短距离。
*/
const int N = 100010;

const int INF = INT_MAX; // 使用INT_MAX作为无穷大
int n, m;
int h[N],e[N],ne[N],w[N],idx;//邻接表储存
int dist[N];
bool st[N];

void add(int a,int b,int c){
    e[idx] = b,w[idx] = c, ne[idx] = h[a],h[a] = idx ++;
}
int dijkstra() {
    memset(dist, 0x3f, sizeof dist); // 初始化距离为无穷大
    dist[1] = 0; // 起点距离设为0
    priority_queue<PII,vector<PII>,greater<PII>> heap; // 小根堆
    heap.push({0,1}); // 将起点加入堆中,起点的距离为0
    while(heap.size()){
        auto t = heap.top();
        heap.pop();

        int ver = t.second,distance = t.first;
        if(st[ver]) continue; // 如果节点已确定最短路径,则跳过
        
        //对当前节点 ver 的所有邻接节点进行遍历和更新
        for(int i = h[ver]; i != -1; i = ne[i]){
            int j = e[i];
            if(dist[j] > distance + w[i]){
                dist[j] = distance + w[i]; // 更新距离
                heap.push({dist[j],j}); // 将新的距离和节点加入堆
            }
        }
    }
    if (dist[n] == INF) return -1; // 如果终点不可达,返回-1
    return dist[n]; // 返回终点的最短距离
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    memset(h, -1, sizeof h); // 初始化邻接表的头节点数组为-1
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c); // 添加边到邻接表
    }
    int t = dijkstra(); // 计算最短路径
    cout << t << '\n'; // 输出结果
    return 0;
}

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

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

相关文章

Nginx 部署前端 Vue 项目实战指南

一、环境准备 1. 安装 Nginx 首先&#xff0c;需要在服务器上安装 Nginx。Nginx 是一款轻量级、高性能的 HTTP 和反向代理服务器。安装方式因操作系统而异。 Linux 系统&#xff08;以 Ubuntu 为例&#xff09;&#xff1a; sudo apt-get update sudo apt-get install nginxWi…

MacOS下WKWebView设置背景透明问题

业务场景需要设置WKWebView的背景内容透明&#xff0c;查询到一般有两种方法&#xff1a; [self.webView setValue: NO forKey: "drawsBackground"]; [self.webView setValue:[NSNumber numberWithBool: YES] forKey:"drawsTransparentBackground"]; 这…

ZBrush入门使用介绍——13、Dynamesh

大家好&#xff0c;我是阿赵。   继续介绍ZBrush的用法。这次介绍一个可以给模型重新布线和生成形状的工具&#xff0c;叫做Dynamesh。 1、 重新布线 在使用Move笔刷的时候&#xff0c;经常会遇到一个问题 当用Move笔刷把模型拖拽出一个形状的时候&#xff0c;被拖出来的部…

ElasticSearch-倒排索引 文档映射

倒排索引文档映射 已有字段的Mapping修改常用Mapping参数配置Index TemplateDynamic Template 倒排索引 当数据写入 ES 时&#xff0c;数据将会通过 分词 被切分为不同的 term&#xff0c;ES 将 term 与其对应的文档列表建立一种映射关系&#xff0c;这种结构就是 倒排索引 为…

火车票、高铁票如何开具电子发票?12306怎么查询报销凭证是否领取?

火车票、高铁票如何开具电子发票&#xff1f; 众所周知&#xff0c;目前很多消费&#xff08;衣食住行&#xff09;报销都是可以开具电子发票的&#xff0c;但火车票目前无法开具电子发票。 火车票目前只有纸质报销凭证&#xff0c;报销凭证与之前的车票类似&#xff0c;但是…

数学建模常用工具总结

数学建模常用工具总结 绘图篇pythonMATLABLIVEGAP CHARTSApache EChartsBioLadderHiplot Pro 生物医学可视化平台Graph EditorRAWGraphs 2.0ExcalidrawPPT绘图 配色篇Color SpaceAdobe Color 素材篇手绘素材插画网iconfont-阿里巴巴矢量图标库下面四个都是实物风格的素材&#…

40天的八股文总结

四十天前报名参加了卡哥的八股文训练营&#xff0c;在这四十天中每周都在训练营中打卡&#xff0c;可以通过念出来的方式进行八股文的记忆&#xff0c;同时还可以听到其他训练营中的朋友们的打卡&#xff0c;这让人感觉非常的有动力&#xff0c;每天都有更强烈的记忆八股文的信…

安装win7鼠标键盘不能动原因分析及解决办法

有同学反馈安装win7鼠标键盘不能动这是怎么回事&#xff1f;后来研究该问题主要是原版win7没有集成usb3.0和usb3.1驱动导致&#xff0c;下面小编就教大家安装win7鼠标键盘不能动原因分析及解决方法。 安装win7鼠标键盘不能动原因分析&#xff1a; 原因&#xff1a;研究后发现是…

Quartz.Net_快速开始

简述 Quartz中主要分为三部分&#xff0c;JobDetail、Trigger、Scheduler&#xff0c;分别是任务、触发器、调度器&#xff0c;三者的关系为&#xff1a;Trigger控制JobDetail的执行时间和频率&#xff0c;而Scheduler负责将具体的Trigger与具体的JobDetail绑定 1.安装Quartz…

无需后端也能测试 CRUD:Mock.js 的强大功能

前言 在前端开发中&#xff0c;数据的增删改查&#xff08;CRUD&#xff09;操作是最常见的需求之一。 然而&#xff0c;在后端接口尚未就绪的情况下&#xff0c;前端开发者往往需要一种方法来模拟这些操作。 Mock.js 作为一个强大的前端数据模拟库&#xff0c;可以帮助开发…

Ubuntu 上启用 swap 内存,提高运行效率!

Ubuntu 24.04 是一个功能强大的操作系统&#xff0c;但有时你的电脑可能会在运行多个应用程序时耗尽内存。这会降低系统的运行速度和效率。在这种情况下&#xff0c;添加交换内存会有所帮助。交换内存作为一个额外的内存资源&#xff0c;您的计算机可以顺利处理更多的任务。 在…

如何用一次推送,毁掉一个公司?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;可以私有化部署&#xff0c;对中文的支持非常友好&#xff0c;是专为中国程序员和企业推出的企业级一体化 DevOps 平台&#xff0c;一键就能安装成功。安装详情可以查看官网指南。 本文分享如何使用极狐GitLab 17.2 发布的密钥…

这样的配置,才够格做“黑悟空”“天命人”

《黑神话&#xff1a;悟空》取材自中国古典名著《西游记》&#xff0c;玩家将扮演齐天大圣孙悟空&#xff0c;体验一段惊心动魄的冒险旅程&#xff0c;被媒体誉为中国首款“3A游戏”。 在《黑神话&#xff1a;悟空》发布并风靡全球之际&#xff0c;唯迈医疗的 Phoenix 210 亦同…

【渗透工具箱】灵兔宝盒-Rabbit_Treasure_Box_V1.0.1

Rabbit_Treasure_Box_V1.0.1 是一款基于Windows操作系统的渗透工具箱&#xff0c;旨在提供一个开箱即用的渗透测试工具集合。该工具箱通过 Dawn Launcher 进行管理&#xff0c;支持一键备份和更新。它包含了脚本类工具&#xff0c;在Windows中启动&#xff0c;集成了在线安全工…

Debug-026-el-upload照片上传-编辑页回显照片并且支持再上传的实现方案

前言&#xff1a; 在之前写的一篇文章《Debug-022-el-upload照片上传-整体实现回顾》中回顾了整体的借助el-upload实现了照片上传的功能。现在业务中增加了一项需求&#xff0c;我们的表单一般是分为“新增页”和“编辑页”的&#xff0c;这里新需求希望可以在编辑页中实现对“…

Java项目: 基于SpringBoot+mysql大学生入学审核系统(含源码+数据库+开题报告+答辩PPT+毕业论文)

一、项目简介 本项目是一套基于SpringBootmysql大学生入学审核系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、…

PAT (Advanced Level) Practice——1008,1009

1008&#xff1a; 难度&#xff1a;简单 题意&#xff1a; 我们城市最高的建筑只有一部电梯。N个数字表示电梯将按指定顺序停在哪些楼层。电梯上一层需要 6 秒&#xff0c;下一层需要 4 秒。电梯将在每个站点停留 5 秒。对于给定的N个数字&#xff0c;您将计算完成这些请求所…

前后端分离项目遇到的跨域问题解决方案(后端为主)

文章目录 什么是跨域问题&#xff1f;第一种方式 ⇒ 注解解决方案&#xff1a;第二种方式 ⇒ 使用 CorsFilter 方法解决&#xff1a;第三种方式 ⇒ 实现 WebMvcConfigure 接口&#xff0c;添加映射&#xff08;个人推荐&#xff09; 什么是跨域问题&#xff1f; 先说问题&#…

【软件流程】项目开发管理制度(Doc文件)

项目开发管理总体流程 一、总则 二、阶段成果 三、岗位设置 四、项目立项 五、项目计划与监控 六、需求分析 七、总体设计 八、详细设计 九、项目实现 十、项目测试 十一、用户培训 十二、系统上线 十三、系统验收 十四、产品维护 十五、源码和文档 十六、质量检…

Gitee镜像关联GitHub仓库

申请 GitHub 私人令牌 GitHub 私人令牌用于授予 Gitee 读写 Github 仓库的权限。 1&#xff09;登录GitHub&#xff0c;通过 个人头像 > Settings > 下拉左侧菜单栏进入 Developer settings。 2&#xff09;Personal access tokens > Tokens(classic) > Generate …