L2-001-紧急救援*C++(使用Dijkstra算法附带全详细注释)

 
L2-001 紧急救援

分数 25
作者 陈越
单位 浙江大学

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2N500)是城市的个数,顺便假设城市的编号为0 ~ (N1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
 

输出样例:

2 60
0 1 3
 
代码长度限制
16 KB
时间限制
200 ms
内存限制
64 MB

 


解题思路:

从题目信息中可得到"道路长度"和"城市救援队"这俩个玩意,于是可以联想到带权图与最短路径问题,我们可以将城市看作节点,道路看作连接节点之间的路径,而每个城市都有一定数量的救援队(城市的权值)同时每条道路都有距离,与此同时根据该图不可能存在负权值的边,而且起点与终点是固定的,那么根据这些信息我们可以确定本题需要用到Dijkstra算法。

与此同时在Dijkstra算法基础上需要考虑三个变量,一个是最短路径的数量road[],另一个是在最短路径的中救援队最多的数量total[],最后一个是前面俩个前提下的路径path[]

于是我在这道题中还用使用到了优先队列priority_queue函数,后来总结发现其实这道题还有动态规划的思想在里面,如果还打算优化的话还能使用弗洛伊德+迪杰斯特拉组合或最小索引堆来进行算法效率的提升(但由于本人技术实在有限就没考虑那么多了T.T)

代码部分:

 1 //#include<bits/stdc++.h> 不建议使用万能头文件
 2 #include<iostream>
 3 #include<set>
 4 #include<cstring>
 5 #include<queue>
 6 #define ll long long; // 定义long long类型 
 7 using namespace std;
 8 
 9 const int inf = 0x3f3f3f3f;//无穷大 
10 typedef pair<int, int>PII;
11 int n, m, s, d;                        //dis[i]表示当前起点到i点的最短距离 
12 int mp[510][510], vis[510], dis[510];//vis数组的作用是记录每个节点是否已被访问过。在Dijkstra算法中,每次从队列中取出距离起点最短的节点时,需要判断该节点是否已经被访问过。若该节点已被访问过,则可以直接跳过,因为它的最短路径已经求得,不需要再次计算。因此,vis数组可以避免重复计算,提高程序的效率。
13 int total[510], city[510], path[510], road[510];//road记录从起点到每个节点的最短路径数量,path[i]表示i节点的最短前驱节点,total[i] 表示从起点到i节点的最短路径的救援人员数总和 
14 
15 void dijkstra()
16 {
17     memset(dis, 0x3f, sizeof(dis)); // 初始化距离为无穷大 
18     total[s] = city[s]; // 初始点的总点权值为其本身的点权值 
19     path[s] = -1; // 初始点没有前驱节点 
20     road[s] = 1; // 初始点到达方式为一种 
21     dis[s] = 0; // 起点到起点的距离为0 
22     //采用优先队列的路线是:先摸出第一条最短路径,(因为这道题还需要找相同距离的可能以及相同距离中救援队最多的情况)所以还需要依次再摸出第二条第三条...的最短路径 
23     //也就是说最短路径的下一个最短 节点会被优先访问 
24     priority_queue<PII>q;//优先队列当存储的是一组数据时默认第一个数据是权值,存储的是pair类型(距离,节点) 用堆q存储最短距离节点(当节点数量多时用最短距离优先队列可以提高效率) 
25     q.push({ 0,s });// 将起点加入队列 
26     while (q.size())
27     {
28         int now = q.top().second; // 取出队首元素的节点 
29         q.pop(); // 删除队首元素 
30         if (vis[now]) // 若当前节点已被访问过,则跳过 
31             continue; 
32         vis[now] = 1; // 标记当前节点已被访问 
33         for (int i = 0; i < n; i++)
34         {
35             if (mp[now][i])//(判断now到i是否存在路径)用now节点去访问所有的节点来找到最短路径 
36             {
37                 if (dis[i] > dis[now] + mp[now][i]) // (dis[i]在i被访问前都是无穷大)若通过当前节点到达其它节点更近,则更新信息 
38                 { 
39                     path[i] = now; // 更新前驱节点(i的前驱节点为now) 
40                     total[i] = total[now] + city[i]; // 更新总点权值(到i的救援队总数为now的总和+城市i的数量) 
41                     dis[i] = dis[now] + mp[now][i]; // 更新距离 
42                     road[i] = road[now]; // *更新到达方式 
43                     if(i!=d)
44                         q.push({ -dis[i],i }); // 将更新后的节点加入队列中 (采用负数是因为希望最近的被优先处理,使得更小的距离对应更高的优先级。) 
45                 }
46                 else
47                 {
48                     if (dis[i] == dis[now] + mp[now][i]) // 若路径相同,则更新路径数量和总点权值 
49                     {
50                         //也就是说在 "now到i的距离mp[now][i]+now的最短距离dis[now]等于i的最短距离dis[i]" 的前提下从起点到i的新的最短路径数量等于 起点到i的最短路径数road[i]加上起点到now的最短路径数road[now]
51                         //最短路径树的延长(可以画张图理解) 
52                         road[i] += road[now]; // ***更新路径数量 
53                         if (total[i] < total[now] + city[i]) // 若当前节点的总点权值更大,则更新 
54                         {
55                             total[i] = total[now] + city[i];
56                             path[i] = now;
57                         }
58                     }
59                 }
60             }
61         }
62     }
63 }
64 
65 int main()
66 {
67     cin >> n >> m >> s >> d;
68     for (int i = 0; i < n; i++)
69         cin >> city[i]; // 输入每个城市的点权值 
70     for (int i = 0; i < m; i++)
71     {
72         int x, y;
73         cin >> x >> y;
74         cin >> mp[x][y];
75         mp[y][x] = mp[x][y]; // 无向图,因此需要将两个方向的边权值都赋值 
76     }
77 
78     dijkstra(); // 调用Dijkstra算法求解最短路径 
79     int now = d;
80     vector<int>answer; // 存储最短路径 
81     while (now != -1)
82     {
83         answer.push_back(now);
84         now = path[now]; // 逆序遍历路径,找到所有经过的节点 
85     }
86     cout << road[d] << " " << total[d] << endl; // 输出到达终点的路径数量和总点权值 
87 
88     for (int i = (int)answer.size() - 1; i > 0; i--)
89         cout << answer[i] << " ";
90     cout << answer[0]; // 输出从起点到终点的最短路径 
91 
92     return 0;
93 }

 

原文链接:https://www.cnblogs.com/shenyuRin/p/17266880.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:L2-001-紧急救援*C++(使用Dijkstra算法附带全详细注释) - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • 【Visual Leak Detector】源码编译 VLD 库

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 源码的编译。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. VLD 库的依赖文件 2. 源码编译生成 VLD 库 3. 配置环境变量 4. 使用 VLD 库 1. VLD 库的依赖文件 以 vld2.5.1 版本为例,下载源码 后,源码包中各文件的用途可看本人另一…

    C++ 2023年4月24日
    00
  • STL容器之queue

    是什么 循环队列, FIFO先进先出 怎么用 初始化 //C11 deque<int> deq{1,2,3,4,5}; //拷贝构造,可以拷贝deque queue<int> que(deq); //100个5 queue<int> que2(100,5); //运算符重载 que2 = que; 操作 //队尾添加元素 …

    C++ 2023年4月17日
    00
  • 【Visual Leak Detector】源码调试 VLD 库

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 源码的调试。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. VLD 库源码调试步骤 1.1 设置为启动项目 1.2 设置调试程序 1.3 设置输出目录 1.4 拷贝 vld 依赖文件 1.5 加断点调试 2. 注意事项 1. VLD 库源码调试步骤 以 vld2.…

    C++ 2023年5月7日
    00
  • C++的对象和类

    一、问题引入 区分面向过程编程和面向对象编程的最大的特性就是 类,类是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组合成一个整洁的包。 那么如何声明类、定义类、调用类? 以 C++ Primer Plus:中文版 (第六版) 的股票类举例说明。 二、解决过程 2-1 类抽象 股票类的抽象化 获得股票 增持股票 卖出股票 更新股票价格…

    C++ 2023年4月17日
    00
  • 第三部分:Spdlog 日志库的实现原理

    Spdlog 是一个快速、异步的 C++ 日志库,被广泛应用于 C++ 项目中。在这篇文章中,我们将探讨 Spdlog 日志库的实现原理。 Spdlog 的结构 Spdlog 由五个主要组件构成:Loggers、Sinks、Formatters、Async Logger 和 Registry。每个组件都扮演着不同的角色,共同协作记录并输出日志消息。 Logg…

    C++ 2023年4月18日
    00
  • 【Visual Leak Detector】核心源码剖析(VLD 1.0)

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇对 VLD 1.0 源码做内存泄漏检测的思路进行剖析。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 源码获取 2. 源码文件概览 3. 源码剖析 3.1 注册自定义 AllocHook 函数 3.2 存储调用堆栈信息 3.3 生成泄漏检测报告 4. 其他问题 4.1 如何区分…

    C++ 2023年4月27日
    00
  • 【Visual Leak Detector】Release 模式下使用 VLD

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍如何在 Release 模式下使用 VLD。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 思路概述 2. 在 QT 中实践 1. 思路概述 要在 RELEASE 模式下使用 VLD,必须在包含头文件 vld.h 前预先定义 VLD_FORCE_ENABLE 宏(参考 VL…

    C++ 2023年4月17日
    00
  • luogu_P1040 [NOIP2003 提高组] 加分二叉树

    P1040 [NOIP2003 提高组] 加分二叉树 – 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意:给你一颗中序遍历为1到n的二叉树,和每个节点的val。树的值=左子树的值×右子树的值+根的val,空树值为1,求整个树最大值和这个值树的前序遍历。 题解:区间dp。dp[l][r]表示最大值,root[l][r]表示最大值的根,枚举区…

    C++ 2023年4月27日
    00
合作推广
合作推广
分享本页
返回顶部