L2-001 紧急救援 (25 分)
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式
输入第一行给出\(4\)个正整数\(N、M、S、D\),其中\(N(2 \leqslant N \leqslant 500)\)是城市的个数,顺便假设城市的编号为\(0 \sim (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
作者:陈越
单位:浙江大学
代码长度限制:16 KB
时间限制:200 ms
内存限制:64 MB
Solution
思路来自Fool_one的L2-001 紧急救援
1.本题题意为在所有最短路中寻找一条点权和最大的路径,并且要求最短路计数和打印路径;
哇塞,真是大杂烩,单源最短路模板题非你莫属!
2.
设num[v]
为起始点到\(v\)点的最短路条数;
设sum[v]
为起始点到\(v\)点的最大点权和;
设s[v]
为\(v\)点的点权;
设path[v]
为当前最优方案下\(v\)点往前的点;
3.对于通常情况下的dis[v]>dis[u]+w
:
dis[v]
代表起始点到\(v\)点的最短路长度,w
为当前边\(u \rightarrow v\)的边权;
首先是路径更新num[v]=num[u]
,显然因为当前路径最优,更新最短路条数;
其次是点权更新sum[v]=sum[u]+s[u]
,理由同上;
之后是路径更新path[v]=u
,理由同上;
最后将更新点入队;
4.因为要求最短路计数,所以需要对dis[v]==dis[u]+w
的点进行特判:
显然有num[v]+=num[u]
(即最短路可以从\(u\)处继承);
并且,检查更新sum[v]
是否大于sum[u]+s[v]
,如果sum[v]<sum[u]+s[v]
,说明当前走法的点权和最大,那么进行更新,并且路径记录path[v]
也要更新;
此处无需入队,因为当前最短路本身为最优状态,入队也会因为vis[v]=1
而无功而返;
5.最后打印路径,直接从path[D]
往前反推即可;
std.cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int N=505;
int n,m,S,D,s[N],num[N],sum[N],path[N],ans[N],dis[N];
bool vis[N];
vector<int>e[N],w[N];
struct node{
int u,w;
bool operator <(const node&x) const{
return w>x.w;
}
};
priority_queue<node>q;
void Dijkstra(){
memset(dis,0x3f,sizeof(dis));
dis[S]=0; num[S]=1;
q.push(node{S,0});
while(!q.empty()){
int u=q.top().u; q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int W,v,i=0;i<e[u].size();++i){
W=w[u][i];
v=e[u][i];
if(dis[v]==dis[u]+W){
num[v]+=num[u];
if(sum[v]<sum[u]+s[v]){
sum[v]=sum[u]+s[v];
path[v]=u;
}
} else if(dis[v]>dis[u]+W){
dis[v]=dis[u]+W;
num[v]=num[u];
sum[v]=sum[u]+s[v];
path[v]=u;
q.push(node{v,dis[v]});
}
}
}
}
int main(){
scanf("%d %d %d %d",&n,&m,&S,&D);
for(int i=0;i<n;++i){
scanf("%d",&s[i]);
sum[i]=s[i];
}
for(int u,v,W,i=1;i<=m;++i){
scanf("%d %d %d",&u,&v,&W);
e[u].push_back(v);
w[u].push_back(W);
e[v].push_back(u);
w[v].push_back(W);
}
Dijkstra();
int Last=D;
while(Last!=S){
ans[++ans[0]]=Last;
Last=path[Last];
}
ans[++ans[0]]=Last;
printf("%d %d\n",num[D],sum[D]);
for(int i=ans[0];i>=1;--i) printf("%d%c",ans[i],i==1 ? '\0' : ' ');
return 0;
}