1003 Emergency (25 分)
As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.
Input Specification:
Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.
Output Specification:
For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.
Sample Input:
5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
Sample Output:
2 4
第一步 所需变量和常量。
常量:const int inf=600*600; 这里600 *600 大概猜的,最多500结点,但是结点之间路径长度范围没给,就瞎猜了
int N,M,start,last; N是结点数,M是边数 start 和last分别为起点和终点
int dis[N]= {0};//用来记录起点到其他店的最短距离,
int sign[N]= {0}; sign是记录那些结点访问了
int sn[N][N];sn是边,记得是双向的。
上面的变量是Dijkstra算法基本变量,下面变量是本题所需的特殊变量
int tiao[N]= {0};记录起点到其他结点最短路径的条数
int hasNum[N]; hasNum是每个城市有多少队伍
int cacheNum[N]= {0};cacheNum是在循环时候,保存起点到其他结点最短路径经过城市的队伍总和
第二 变量初值问题
tiao[start]=1;
cacheNum[start]=hasNum[start];起点这里需要赋值,这样在第一次循环时候,我们让第一次循环距离起点最短的点就是起点(就是在声明sign数组后,不把sign[start]赋值为1),这样在第一循环时候可以把和起点直接相连结点的 tiao cacheNum 都计算出来了,很方便(如果不这样,就两个循环,对tiao 和cacheNum进行赋值也行,就是把和起点直接相连结点的tiao 赋值为1,cacheNum赋值为起点和当前结点队伍数目之和)
第三 注意二维数组赋值非0,只能用fill,不能使用memset
第四 本题对于迪杰斯特拉算法的添加内容的关键
if(dis[pos]+sn[pos][i]<dis[i]) {
dis[i]=dis[pos]+sn[pos][i];
tiao[i]=tiao[pos];
cacheNum[i]=cacheNum[pos]+hasNum[i];
当我们找到一条比现在到i结点更近的路径,那么从起点到达结点i的最短路径数目应该就是起点到达结点pos的最短路径数目了,千万注意不是1,是和上个结点的最短路径数目一样的
} else if(dis[pos]+sn[pos][i]==dis[i]) {
tiao[i]+=tiao[pos];
cacheNum[i]=max(cacheNum[pos]+hasNum[i],cacheNum[i]);
当我们找到一条和现在到i结点一样近的路径,那么从起点到达结点i的最短路径条数就是当前i的最短路径数目加上起点到达结点pos的最短路径数目。
}
#include <iostream>
#include <cstdio>
#include <string.h>
#include <vector>
#include <algorithm>
#include <cmath>
#include <queue>
#include <sstream>
#include <climits>
using namespace std;
const int inf=600*600;
int main() {
int N,M,start,last;
cin>>N>>M>>start>>last;
int hasNum[N];
for(int i=0; i<N; i++) {
cin>>hasNum[i];
}
int dis[N];
int sn[N][N];
fill(sn[0], sn[0] +N * N, inf);
fill(dis, dis +N, inf);
//memset(dis, inf, N* sizeof(int));这个可以给一维数组赋值
dis[start]=0;
for(int i=0; i<M; i++) {
int a,b;
cin>>a>>b;
cin>>sn[a][b];
sn[b][a]=sn[a][b];//记得是双向的
}
for(int i=0; i<N; i++) {
if(i!=start)
dis[i]=sn[start][i];
}
int cacheNum[N]= {0},sign[N]= {0};
cacheNum[start]=hasNum[start];
int tiao[N]= {0};
tiao[start]=1;//没有这个无法保证在第一次循环中直连的点的条数是1
for(int q=0; q<N; q++) {
int minDis=inf;
int pos=-1;
for(int i=0; i<N; i++) {
if(sign[i]==0) {
//必须先让自身过一遍,这样其他直达的cacheNum就有数字了
if(minDis>dis[i]){
minDis=dis[i];
pos=i;
}
}
}
sign[pos]=1;
for(int i=0; i<N; i++) {
if(i!=pos&&i!=start&&sign[i]==0) {
if(dis[pos]+sn[pos][i]<dis[i]) {
dis[i]=dis[pos]+sn[pos][i];
tiao[i]=tiao[pos];
cacheNum[i]=cacheNum[pos]+hasNum[i];
} else if(dis[pos]+sn[pos][i]==dis[i]) {
tiao[i]+=tiao[pos];
cacheNum[i]=max(cacheNum[pos]+hasNum[i],cacheNum[i]);
}
}
}
}
cout<<tiao[last]<<' '<<cacheNum[last];
return 0;
}