天梯赛:L2-001 紧急救援
题目
L2-001 紧急救援
题目描述
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式
输入第一行给出 4 4 4 个正整数 N 、 M 、 S 、 D N、M、S、D N、M、S、D,其中 N N N( 2 ≤ N ≤ 500 2≤N≤500 2≤N≤500)是城市的个数,顺便假设城市的编号为 0 0 0 ~ ( N − 1 ) (N−1) (N−1); M M M 是快速道路的条数; S S S 是出发地的城市编号; D D D 是目的地的城市编号。
第二行给出 N N N 个正整数,其中第 i i i 个数是第 i i i 个城市的救援队的数目,数字间以空格分隔。随后的 M M M 行中,每行给出一条快速道路的信息,分别是:城市 1 1 1、城市 2 2 2、快速道路的长度,中间用空格分开,数字均为整数且不超过 500 500 500。输入保证救援可行且最优解唯一。
输出格式
第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从 S S S 到 D D 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
解题思路
这道题目是一个典型的最短路径问题,但不仅仅是求最短路径,还需要考虑在最短路径的基础上,统计最短路径的数目以及尽可能多地召集救援队。因此,我们需要在Dijkstra算法的基础上进行一些扩展。
Dijkstra算法回顾
Dijkstra算法是一种用于求解单源最短路径的经典算法。它的基本思想是从起点开始,逐步扩展到其他节点,每次选择当前距离起点最近的节点,并更新其邻居节点的距离。
本题的扩展
在本题中,我们不仅需要找到最短路径,还需要:
- 统计最短路径的条数:即从起点到终点的最短路径有多少条。
- 在最短路径的基础上,选择救援队数量最多的路径:即在多条最短路径中,选择救援队数量最多的那条。
为了实现这些功能,我们需要在Dijkstra算法的基础上增加一些额外的信息:
cnt[i]
:表示从起点到城市i
的最短路径条数。nums[i]
:表示从起点到城市i
的最短路径上能够召集的救援队数量。pre[i]
:表示在最短路径中,城市i
的前驱节点,用于最后输出路径。
代码实现
#include<bits/stdc++.h>
using namespace std;
void solve() {
int n, m, s, d;
cin >> n >> m >> s >> d;
// 每个城市的救援队数量
vector<int> w(n);
for (int i = 0; i < n; ++i)
cin >> w[i];
// 图的邻接矩阵表示,初始化为无穷大
vector<vector<int>> g(n, vector<int>(n, 0x3f3f3f3f));
for (int i = 0; i < m; ++i) {
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = c; // 无向图
}
// Dijkstra算法的初始化
vector<int> vis(n, 0); // 标记是否访问过
vector<int> cnt(n, 0); // 最短路径条数
vector<int> nums(n, 0); // 救援队数量
vector<int> dist(n, 0x3f3f3f3f); // 最短距离
vector<int> pre(n, -1); // 前驱节点
dist[s] = 0; // 起点到自己的距离为0
cnt[s] = 1; // 起点到自己的最短路径条数为1
nums[s] = w[s]; // 起点到自己的救援队数量为w[s]
// Dijkstra算法主循环
for (int i = 0; i < n; ++i) {
int x = -1;
// 找到当前未访问的节点中距离起点最近的节点
for (int j = 0; j < n; ++j) {
if (!vis[j] && (x == -1 || dist[x] > dist[j]))
x = j;
}
if (x == -1) break; // 所有节点都已访问
vis[x] = 1; // 标记为已访问
// 更新邻居节点的距离
for (int j = 0; j < n; ++j) {
if (dist[j] > dist[x] + g[x][j]) {
dist[j] = dist[x] + g[x][j];
cnt[j] = cnt[x]; // 有更优的最短路径,直接更新最短路径条数
nums[j] = nums[x] + w[j]; // 有更优的最短路径,直接更新救援队数量
pre[j] = x; // 更新前驱节点
} else if (dist[j] == dist[x] + g[x][j]) {
cnt[j] += cnt[x]; // 当前更新的距离和先前更新的最短路径距离一致,增加最短路径条数
if (nums[j] < nums[x] + w[j]) {
nums[j] = nums[x] + w[j]; // 距离一致情况下,取最大救援队数量
pre[j] = x; // 取最大救援队数量路径,同时更新前驱节点
}
}
}
}
// 输出最短路径数目以及救援队数目最大的结果
cout << cnt[d] << ' ' << nums[d] << '\n';
// 输出路径
vector<int> path{d};
int idx = d;
while (idx != s) {
idx = pre[idx];
path.push_back(idx);
}
// 逆序输出路径
for (int i = path.size() - 1; i >= 0; i--)
cout << path[i] << " \n"[i == 0];
}
int main() {
cin.tie(nullptr), cout.tie(nullptr);
ios::sync_with_stdio(false);
solve();
return 0;
}