题面略。
思路:其目的是求s(source)到d(destination)的单源最短路径,比较容易想到的是dijikstra算法。
回顾dijisktra算法的流程:将图中所有顶点分为两个集合,一个N,一个U,N表示该集合中的顶点已经访问过,U表示该集合中的顶点还没有访问。
1)初始化:准备一个数组d,d[i]表示从起点到顶点i的最短路径,初始值等于起点与该顶点之间的边的代价cost[s][i],如果并未直接相邻,则令其值为无穷大。
2)遍历顶点:从目前U集合顶点中 d[i] 最小的顶点开始访问,假设这个顶点是 minV,将顶点 minV 加入到N中,再更新顶点 minV 的所有邻居顶点 w 的d[w] = min(d[w], d[minV]+cost[minV][w]).
3)重复步骤2,直到U为空为止。
题解步骤:除了dijisktra的必要初始化之外,由于需要记录最短路径的数目和救援队的数目,先初始化一个amount数组,amount[ i ]表示从s到顶点 i 的最短路径上的最大的救援队伍的数目(值得注意的是,初始化时,amount[s]=team[s], 而其他顶点amount[i] = amount[s]+team[i], 这是因为要到顶点i必然从s出发,所以要加上s中的救援队;不要在准备输出的时候才加上s中的队伍数目,因为有一个检查点是,输入的s和d相同,这样就会出错。);还有一个mind数组,mind[i] 表示从起点s 到顶点i 的最短路径的数目。
根据dijisktra的最优子结构对mind数组,amount数组和d数组进行更新。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int t[501];
int amount[501];
int mind[501];
int cost[501][501];
int ds[501];
int ischecked[501];
int n,m,s,d;
const int inf=(1<<30)-1;
void dij(){
amount[s]=t[s];
mind[s]=1;
for(int i=0;i<n;i++){
ds[i]=cost[s][i];
ischecked[i]=false;
if(ds[i]!=inf&&i!=s){
mind[i]=1;
amount[i]=t[i]+amount[s];
}
}
ischecked[s]=true;
for(int node=0;node<n;node++){
int minV=-1;
int minC=inf;
//选最短距离最小的顶点
for(int i=0;i<n;i++){
if(!ischecked[i]&&ds[i]<minC){
minC=ds[i];
minV=i;
}
}
if(minV==-1){
continue;
}
//访问此顶点
ischecked[minV]=true;
for(int i=0;i<n;i++){
//查看此顶点的所有未访问邻居
if(!ischecked[i]&&cost[minV][i]<inf){
//如果两种路径相等,则更新最短路径的数目,更新队伍的数目
if(ds[i]==ds[minV]+cost[minV][i]){
mind[i]+=mind[minV];
amount[i]=max(amount[i],amount[minV]+t[i]);
}
//如果比原路径小,更新为最短路径,最短路径数目为1.
else if(ds[i]>ds[minV]+cost[minV][i]){
ds[i]=ds[minV]+cost[minV][i];
mind[i]=mind[minV];
amount[i]=amount[minV]+t[i];
}
}
}
}
}
int main(void){
scanf("%d %d %d %d",&n,&m,&s,&d);
for(int i=0;i<n;i++){
scanf("%d",&t[i]);
}
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
cost[i][j]=cost[j][i]=inf;
}
}
for(int i=0;i<m;i++){
int c1,c2,c;
scanf("%d %d %d",&c1,&c2,&c);
if(c<cost[c1][c2]){
cost[c1][c2]=cost[c2][c1]=c;
}
}
dij();
printf("%d %d",mind[d],amount[d]);
}