【PAT甲级题解记录】1003 Emergency (25 分)
前言
Problem:1003 Emergency (25 分)
Tags:单源最短路径 dijkstra
Difficulty:
剧情模式想流点汗想流点血死而无憾Address:1003 Emergency (25 分)
问题描述
-
给定一个城市间构成的一个无向图,每个点上都有一个救援人手数量,一个救援队需要从点 C1C1C1 到点 C2C2C2 ,途经的城市中的救援人手求最短路径的数量以及满足路径最短的途经的最大人手数量和。
-
简单来说,就是一个带权点的无向图,求其单源最短路径的数量以及最短路径情况下的最大路径点权和。
解题思路
-
对于单源最短路径(这道题也应该不会出现负边),一般我们最常用的就是dijkstra算法,但这里不单单是求最短路径,但是依旧可以用这个算法,算是dijkstra求最短路径的变题。最短路径我也总结了一个模板在这里:最短路径一般算法总结(板子),可以直接复制下来改。
-
在此基础上,为了求最短路径数量,我们加入了类似dp的思想:
-
cnt[i]cnt[i]cnt[i] 表示到 iii 点的最短路径数量。然后在dijkstra算法的每一次更新路径长度数组dist时更新每一个点的cnt值即可。
-
num[i]num[i]num[i] 表示当前最短路径下到 iii 点的最大救援人员,同样在更新最新dist时更新num值,但是不同的是这时候的更新时有条件的(肯定要人员更多才更新)。
-
即分为俩种情况:
-
搜索到相同路径长度的前节点时:
cnt[i] = cnt[i_pre]+cnt[i];
若当前方案救援人数更多时:
num[i] = num[i_pre]+num_of_rescue[i];
-
搜索到更短路径长度的前节点时:
cnt[i] = cnt[i_pre]; num[i] = num[i_pre]+num_of_rescue[i];
-
-
这道题在atcoder上也出现过,但那个版本更简单:https://atcoder.jp/contests/abc211/tasks/abc211_d
参考代码
#include<iostream>
#include<vector>using namespace std;
int N; // number of cities
int M; // number of roads
int C1, C2; // start, end
vector<int> teams(505, 0); // number of rescue teams in the i-th city
vector<int> dist(505, 0x3f3f3f3f); // 初始化为大值,表示无路径
vector<bool> in(505, false);
vector<vector<int> > MAP(505);
vector<int> cnt(505, 0);
vector<int> teams_sum(505, 0); // 按照当前最短路径,到某一点可遇到的救援队最多数量void init() {cin >> N >> M >> C1 >> C2;for (int i = 0; i < N; i++) {cin >> teams[i];}for (int i = 0; i < N; i++) {MAP[i].resize(N, 0x3f3f3f3f);}for (int i = 0; i < M; i++) {int c1, c2, L;cin >> c1 >> c2 >> L;MAP[c1][c2] = L;MAP[c2][c1] = L;}
}void dijkstra() {dist[C1] = 0; // 初始化dist数组,这样可以确保第一次循环可以顺利得出距离集合最短的路径,即起点本身teams_sum[C1] = teams[C1];cnt[C1] = 1;int times = N;while (times--) { // loop for N timesint min_dist = 0x3f3f3f3f;int min_city = -1;// find min_distfor (int i = 0; i < N; i++) {if (!in[i] && dist[i] < min_dist) {min_dist = dist[i];min_city = i;}}if (min_city == C2) break;in[min_city] = true;// update distfor (int i = 0; i < N; i++) {int new_dist_toi = dist[min_city] + MAP[min_city][i]; // 通过 min_city 到 i 的 最短路径int new_teams_num_toi = teams_sum[min_city] + teams[i]; // 通过 min_city 到 i 的 最大救援队数量if (!in[i] && new_dist_toi < dist[i]) {dist[i] = new_dist_toi;teams_sum[i] = new_teams_num_toi;cnt[i] = cnt[min_city];} else if (!in[i] && new_dist_toi == dist[i]) { // 注意一定要加 else,否则前一个 if 会影响该判断cnt[i] = cnt[min_city] + cnt[i];if (new_teams_num_toi > teams_sum[i]) {teams_sum[i] = new_teams_num_toi;}}}}
}void solution_1003() {init();dijkstra();cout << cnt[C2] << " " << teams_sum[C2] << endl;
}
int main() {solution_1003();return 0;
}
总结
没啥好说的算是比较基础的最短路径问题,但是要记住理解也需要花点功夫,毕竟再熟悉的算法隔一段时间后也可能会忘。
再把最短路径的板子放一遍:最短路径一般算法总结(板子)