题记
本题在最基础的单源结点最短距离的基础上增加了点权,并且让求出最短距离路线的条数。
关键思路(如何求最短路线条数和召集最多的救援人员)
假设当前到原点s最短距离的点是u,u再往外走一步的结点记为v,u的最短距离是d[u],它的最短路线数目是num[u],它所在位置(距离最短的前提下)最多能到达的救援人员数目是w[u],v点同样,G[u][v]记录了u到v的距离此时u的相关信息是已经是最优的了
1.如果d[u]+G[u][v]<d[v],说明v借助u可以达到更优,那么d[v]=d[u]+G[u][v];
v的最短路线数目被覆盖为num[u],即num[v]=num[u],因为原来保存的不是当前最优的,原来的那些路线我们都不需要了;此时能到达救援人员数目为w[v]=w[u]+weight[v],因为距离最短是主要的,而救援人员的数目是第二条件,在距离更短的情况下,即便救援人员数目不是最多的也要用这条路线;
2.如果d[u]+G[u][v]=d[v],那么此时最短距离用不着更新;
v的最短路线数目就等于在原来已有的最短路线数目基础上再增添num[u]数目的最短路线,这些最短路线都可以用;(注意不是加1)
救援人员数目此时需要判断一下,原有线路当中救援人员数目多还是经过u的路线所能召集到的救援人员数目多,取最大值;
另外需要注意的是一定要注意,不管是找当前距离起点s最短的u还是从u出发更新相邻顶点,都一定要先判断结点是否访问过.
代码如下
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxv=500+10;
const int INF=1000000000;
//输入
//城市数量、边数、起点、终点
int n,m,st,ed;
//图、各城市点权
int G[Maxv][Maxv],weight[Maxv];
//最大点权,最短距离,最短距离路线数
int w[Maxv],d[Maxv],num[Maxv];
bool visit[Maxv]={false};
void dijstra(int s){//起点为s
memset(num,0,sizeof(num));
memset(w,0,sizeof(w));
fill(d,d+Maxv,INF);
d[s]=0;
num[s]=1;
w[s]=weight[s];
for(int i=0;i<n;i++){//一共n个顶点,循环n次
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(visit[j]==false&&d[j]<MIN){
u=j;
MIN=d[j];
}
}
if(u==-1) //剩下的结点和起点s不连通
return;
visit[u]=true;
for(int v=0;v<n;v++){
if(visit[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){//借助u可以使v的最短距离更优
d[v]=d[u]+G[u][v];
w[v]=w[u]+weight[v];
num[v]=num[u];
}
else if(d[u]+G[u][v]==d[v]){
if(w[u]+weight[v]>w[v]){
w[v]=w[u]+weight[v];
}
num[v]+=num[u];
}
}
}
}
}
int main()
{
scanf("%d %d %d %d",&n,&m,&st,&ed);
for(int i=0;i<n;i++)
scanf("%d",&weight[i]);
fill(G[0],G[0]+Maxv*Maxv,INF);
int u,v;
for(int i=0;i<m;i++){
scanf("%d %d",&u,&v);
scanf("%d",&G[u][v]);
G[v][u]=G[u][v];
}
dijstra(st);
printf("%d %d\n",num[ed],w[ed]);
return 0;
}