作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);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
结尾无空行
#include <iostream>
#define INF 0x3f3f3f3f
#define MAXN 500
using namespace std;
int n, m, s, d;
int totalLen[MAXN]; //记录起点S到i城的最短路径
int totalRescue[MAXN]; //记录起点S到i城召集最多救援队数
int rescue[MAXN]; //记录i城的救援队数
int Len[MAXN][MAXN]; //记录两城之间路径长度
int cnt[MAXN]; //记录最短路径的条数
int pre[MAXN]; //记录i城的前驱
int vis[MAXN]{false}; //标记是否访问
void print(int u){
// 顶点 == 起点 (递归边界,找到起点)
if(u == s){
cout << u;
return;
}
// 找前驱结点 递归输出 (递归式)
print(pre[u]);
cout << " " << u;
}
void Dijkstra(int s){
totalLen[s] = 0;
totalRescue[s] = rescue[s];
cnt[s] = 1;
// 更新N-1次,源点已更新
for(int i = 0; i < n-1; i++){
int u, Min = INF;
// 找最近邻接点
for(int j = 0; j < n; j++)
if(vis[j] == false && totalLen[j] < Min)
u = j, Min = totalLen[j];
vis[u] = true;
// 连通图 不需要判断不连通而退出
for(int v = 0; v < n; v++){
// 以最近邻接点u为中介 顶点未遍历过 && 路径更短
if(vis[v] == false && totalLen[v] > totalLen[u] + Len[u][v]){
// 更新源点s到其他顶点路径长度 = s->u + u->v
totalLen[v] = totalLen[u] + Len[u][v];
// 救援队
totalRescue[v] = totalRescue[u] + rescue[v];
// 最短路径数
cnt[v] = cnt[u];
// 途经城市
pre[v] = u;
}
// 路径长度相同
else if(vis[v] == false && totalLen[v] == totalLen[u] + Len[u][v]){
// 最短路径不唯一
cnt[v] += cnt[u];
// 更多救援队
if(totalRescue[v] < totalRescue[u] + rescue[v]){
totalRescue[v] = totalRescue[u] + rescue[v];
pre[v] = u;
}
}
}
}
};
int main() {
// 初始化路径长度及城市最短路径
fill(*Len, *Len + MAXN * MAXN, INF);
fill(totalLen, totalLen + MAXN, INF);
cin >> n >> m >> s >> d;
// 读取救援队数量及路径长度
for(int i = 0; i < n; i++){
cin >> rescue[i];
}
for(int i = 0; i < m; i++){
int c1, c2, len;
cin >> c1 >> c2 >> len;
Len[c1][c2] = Len[c2][c1] = len;
}
Dijkstra(s);
// 输出最短路径及最多救援队数量
cout << cnt[d] << " " << totalRescue[d] << endl;
// 输出途径城市
print(d);
}
cnt[to] += cnt[pass];
想了想为什么这里要累加之前的路径,如果没有初始化的时候,所有cnt不都是0吗。所以在更新路径长度和相同路径的判断里加了这句代码,看看发生了什么。
cout << totalLen[to] << " " << pass << " " << to << " " << cnt[pass] << " " << cnt[to] << endl;
可以看到从源点0到终点3时更新的情况。相同路径长度的情况有0->3和1->3,两者路径长度都是最前面的3。这时候到达目的地3就有2条路径,而如果其他点经过3到其他终点,就会加上2。后面的结点以此类推,所以可以知道为什么要 += 。