7-37 城市间紧急救援 (25 分)

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出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<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Inf=999999999;
const int maxn= 505;
int G[maxn][maxn];
int dis[maxn],ans[maxn],vis[maxn],num[maxn],sum[maxn],path[maxn];
int n,m,s,d;
void Dijkstra(){
	for(int i=0;i<n;i++)
	dis[i]=G[s][i];
	vis[s]=1;
	ans[s]=1;
	for(int k=1;k<n;k++){
		int mint=Inf,u=-1;
		for(int i=0;i<n;i++){
			if(!vis[i]&&dis[i]<mint){
				mint=dis[i];
				u=i;
			}
		}
		if(u==-1) return ;
		vis[u]=1;
		for(int i=0;i<n;i++){
			if(!vis[i]&&dis[i]>dis[u]+G[u][i]){
				dis[i]=dis[u]+G[u][i];
				path[i]=u;
				ans[i]=ans[u];
				sum[i]=sum[u]+num[i];
			}
			else if(!vis[i]&&dis[i]==dis[u]+G[u][i]){
				ans[i]+=ans[u];
				if(sum[i]<sum[u]+num[i]){
					path[i]=u;
					sum[i]=sum[u]+num[i];
				}
			}
		}
	}
}
void print(int x){
	if(x==-1) {
	   printf("%d",s);
	   if(s!=d) printf(" ");
	   else printf("\n");
       return ;
	}
	else print(path[x]);
	printf("%d",x);
	if(x!=d) printf(" ");
	else printf("\n");
}
int main(){
	scanf("%d%d%d%d",&n,&m,&s,&d);
	for(int i=0;i<n;i++){
		scanf("%d",&num[i]);
		sum[i]=num[i];
	    ans[i]=1;	
	    path[i]=-1;
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			if(i!=j)
			G[i][j]=G[j][i]=Inf;
			else
			G[i][j]=G[j][i]=0;
		}
	}
	for(int i=0;i<m;i++){
		int a,b,w;
		scanf("%d%d%d",&a,&b,&w);
		G[a][b]=G[b][a]=w;
	}
	Dijkstra();
	printf("%d %d\n",ans[d],sum[d]+num[s]);
	print(d);
	return 0;
}