题目链接:点击打开链接
L2-001. 紧急救援
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出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
题解:
这是一道最短路的问题,我原来想用最简单的迪杰斯特拉(卿学姐版)的来干掉,不过可能是我对于卿学姐优化过后的最短路理解不够深刻,这道题里最最关键的一个点总是过不了,那就是最短路数。在这个使用了for循环来实现的最短路中我的能够确保每个点最多只会被vis一次,这样就不会出现重复计算的路数了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = (int)507;
const int INF = (int)0x3f3f3f3f;
int jiu[MAXN];
int sum[MAXN];//储存的时救援对的数量
int dis[MAXN];
int ans[MAXN];//记录路数
int pre[MAXN];//记录前一个结点
int E[MAXN][MAXN];
set<int> vis; //储存的是每个点有没有扫过
int main()
{
ios::sync_with_stdio(false);
memset(E,-1,sizeof(E));
int N,M,S,D;
cin >> N >> M >> S >> D;
for(int i = 0;i < N;i ++){
cin >> jiu[i];
sum[i] = jiu[i];
vis.insert(i);
}
for (int i = 1;i <= M;i ++){
int x,y,w;
cin >> x >> y >> w;
E[x][y] = E[y][x] = w;
}
memset(dis,0x3f,sizeof(dis));
memset(pre,-1,sizeof(pre));
//fill (ans,ans+MAXN,1);
dis[S] = 0;
int now = S;
ans[S] = 1;
for (;vis.size() > 0;vis.erase(now)){
int mn = INF;
for (int i = 0;i < N;i ++){
if (dis[i] < mn && vis.count(i) == 1){
mn = dis[i];
now = i;
}
}
for (int i = 0;i < N;i ++){
if (E[now][i] != -1){
if (dis[i] > dis[now]+E[now][i]){
dis[i] = dis[now]+E[now][i];
pre[i] = now;
ans[i] = ans[now];
sum[i] = sum[now] + jiu[i];
}else if (dis[i] == dis[now] + E[now][i]){
ans[i] += ans[now] ;
if (sum[i] < sum[now] + jiu[i]){
sum[i] = sum[now] + jiu[i];
pre[i] = now;
}
}
}
}
}
cout << ans[D] << ' ' << sum[D] << endl;
int t = D;
stack<int> st;
while (pre[t] != -1){
t = pre[t];
st.push(t);
}
while (st.size()){
cout << st.top() << ' ';
st.pop();
}cout << D << endl;
}