先来总结一下:
1)Dijkstra算法有两种,一种是提前初始化好数组d[],另外一种是不初始化好,把第一步也计算进去,因为要计算每个经过点的救援队的数量的总和,故选择后一种。
2)在Dijkstra算法的基础上,增加两个数组,一个是num[]数组,一个是w[]数组,分别表示从出发点到 i 结点总共的路的个数,和到达该地区救援队的数量。
3)因为是要召集更多的救援队,所以在d[ k ]=d[ j ]+e[ j ][ k ]的时候应该另外讨论,即num[ k ]=num[ j ]+num[ k ];到达点 k 的路为到达点 j 和到达点 k的路数之和。(只起到路数增加的一个效果) 在此基础上再进行讨论救援队数量总和最多,只有当 w[ k ]<w[ j ]+weight[ k ]时,再找前驱。
4)最后一个点,按照之前的思路来说,打印出来的路径是反着来的(从D->S),所以改变一下刚开始的a1和a2打印出来的路径就反过来了。
题目描述:
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出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
题目大意:
在既有点权又有边权的情况下,求出最短路径,而且有一个条件是,一路上要求召集更多的救援队。(Dijkstra算法)
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int main()
{
int n,m,a1,a2,k1,k2,info;
scanf("%d%d%d%d",&n,&m,&a2,&a1);
int weight[n],e[n][n],d[n];
fill(e[0],e[0]+n*n,inf);
fill(d,d+n,inf);
for(int i=0;i<n;i++)
scanf("%d",&weight[i]);
for(int i=0;i<m;i++){
scanf("%d%d%d",&k1,&k2,&info);
e[k1][k2]=e[k2][k1]=info;
}
int p[n]={0},flag[n]={0},pre=a2,sum=0,num[n]={0},w[n]={0};
d[a1]=0;
p[a1]=-1;
w[a1]=weight[a1];
num[a1]=1;
for(int i=0;i<n;i++){
int min=inf,j=-1;
for(int k=0;k<n;k++)
if(!flag[k]&&d[k]<min){
min=d[k];
j=k;
}
if(j==-1) break;
flag[j]=1;
for(int k=0;k<n;k++){
if(!flag[k]&&e[j][k]!=inf&&d[k]>d[j]+e[j][k]){
d[k]=d[j]+e[j][k];
w[k]=w[j]+weight[k];
num[k]=num[j];
p[k]=j;
}
else if(!flag[k]&&e[j][k]!=inf&&d[k]==d[j]+e[j][k]){
num[k]=num[j]+num[k];
if(w[k]<w[j]+weight[k]){
w[k]=w[j]+weight[k];
p[k]=j;
}
}
}
}
printf("%d %d\n",num[a2],w[a2]);
while(p[pre]!=-1){
printf("%d ",pre);
pre=p[pre];
}
printf("%d",a1);
return 0;
}