作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出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
思路:利用dijksta算法求单源最短路问题,这里还要维护一些额外信息,比如各个点救援队的数量,还有最短路径的条数,根据题意去输出。
易错点:该题目最好用pre存,存每个点之前的点,如果用ne存,存每个点之后的点会出现问题。打印样例情况下的t和j,0->1 0->2 0->3 1->3 是这样的。用ne就直接0指向3,用pre就3到1然后1到0,就不会出现串联的情况。这样才能正确存储路径。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=510;
int w[N],dist[N],g[N][N],path[N],maw[N],pre[N];
//w存每个点救援队数量 g是邻接矩阵用来存储图
//dist数组是dijkstra算法的关键,每个点距离源点的最短距离
//path是以当前点为终点的最短路径的数量
//maw是到当前点 可以召集的救援队最大数量
//pre是存储前一个点
int n,m,s,d;
bool st[N];
void dijkstra()
{
memset(dist,0x3f,sizeof dist);//初始化dist数组
memset(pre,-1,sizeof pre);
dist[s]=0;//定义好源点,源点到源点距离为0
path[s]=1;//题目要求存在,那么最短路径的条数最少是一条
maw[s]=w[s];//起点的召集救援队最大数量就是起点的救援队数量
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=0;j<n;j++)//寻找集合中距离最小的点
{
if(!st[j]&&(t==-1||dist[t]>dist[j]))//这里dist数组不要写错,我经常写成st数组
{
t=j;
}
}
st[t]=true;
for(int j=0;j<n;j++)//用最短点更新其他点距离
{
if(dist[j]>dist[t]+g[t][j])
{
dist[j]=dist[t]+g[t][j];//更新距离
path[j]=path[t];//最短路个数等于前面一个点的最短路个数,因为t到j只有一条路
maw[j]=maw[t]+w[j];//更新召集到的救援队数量
pre[j]=t;//j前面是t
}
else if(dist[j]==dist[t]+g[t][j])
{
path[j]+=path[t];
//这时候最短路个数要加上另一个分支最短路的个数,这里不能直接++,因为另一个分支最短路个数并不确定
if(maw[j]<maw[t]+w[j])
{
maw[j]=maw[t]+w[j];//更新召集到的救援队数量
pre[j]=t;//j前面是t
}
}
}
}
}
int main()
{
cin>>n>>m>>s>>d;
for(int i=0;i<n;i++)scanf("%d",&w[i]);
memset(g,0x3f,sizeof g);//初始化邻接矩阵
while(m--)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
g[b][a]=g[a][b]=min(g[a][b],c);//无向图要存两条,最短路问题如果有重边就取最短
}
dijkstra();//调用dijkstra算法
cout<<path[d]<<" "<<maw[d]<<endl;//按照题意要求输出
vector<int>res;//用vector存储路径
for(int i=d;i!=-1;i=pre[i])
{
res.push_back(i);//这是逆序
}
for(int i=res.size()-1;i>=0;i--)//颠倒顺序
{
cout<<res[i];
if(i!=0)cout<<" ";
}
return 0;
}