解题步骤:
1.初始化:设置mat[][]存放点之间的距离,vis[]存放点的选取情况,people[]存放初始时每个城市的人数,man[]存放到达每个城市的救援队的最多的人数,num[]存放到达每个城市的最多的人数(在最短路径的基础之上),dist[]存放从起点开始到达每个城市的最短的路径(随着每次选取点而更新)
2.核心算法:按照Dijkstra算法思想,从起点出发,不断选择一个点使得通过该点到达其他点的距离比直接通过已知点到达的距离更短,则更新最短距离数组dist[],这里分为两种情况1:确实是通过某个点能使得到达其他点的距离更短,则除了修改最短距离数组dist之外,此时通过该点为跳板达到点的行走方案数量就是到达该跳板点的行走方案数量,而到达该点的人数就是到达跳板点的人数加上该点本来就有的人数。(因为人数是必须以最短距离为基础的,只要有更短的距离,则人数就要修改成那种方案下的人数)2:如果通过选中的点到达其他点的距离和原本已知点达到该点的距离是相等的,则需要增加到达该点的行走方案的总数,以及可能要修改同样是到达该点的时最多可召集的救援队的数量。方案增加的方法是原来到x点的走法数量加上到达跳板点的走法的数量,而人数的修改则是选取原来方案到x的人数和到达跳板点最多人数+x点的人数二者之间的最大值
3.注意点:对于初始化的时候,我们要注意的是除了设置所有从起点开始不可达的点的距离为无穷大(0x3f3f3f3f)之外, 我们巧妙的设置初始点到自己的距离为0,通过这样的方式,第一个选中的跳板是出发点自身,则会为man[],num[]数组进行一遍赋值,这样后续的核心算法的判断和修改过程才能成立
1 #include<iostream> 2 #include<string.h> 3 using namespace std; 4 5 const int Max = 0x3f3f3f3f; 6 const int N = 505; 7 int mat[N][N]; 8 int dist[N]; //记录随着每次获取一个新的点之后从起点开始到每个点的最短距离的最新情况 9 int people[N]; //记录每个城市初始的救援队的人数 10 int num[N]; //记录从起点开始到达某个点的最短路径的条数 11 int man[N]; //记录从起点开始在最短路径的基础上到达某个城市的最多的人数 12 int vis[N]; //记录某个城市是否被走过 1走过 0未走过 13 int n, m, s, t; 14 15 void init(){ 16 for(int i = 0; i < n; i++) scanf("%d", &people[i]); 17 memset(mat, Max, sizeof(mat)); 18 memset(vis, 0, sizeof(vis)); 19 memset(man, 0, sizeof(man)); 20 memset(num, 0, sizeof(num)); 21 for(int i = 1; i <= m; i++){ 22 int x, y, z; 23 scanf("%d%d%d", &x, &y, &z); 24 mat[x][y] = z; 25 mat[y][x] = z; 26 } 27 mat[s][s] = 0; //设置到达自己本身为0 则第一次必定会选择自己 28 for(int i = 0; i < n; i++){ 29 dist[i] = mat[s][i]; 30 } 31 num[s] = 1; //设置初始到达起点的走法有1条 32 man[s] = people[s]; //设置到达起点的可以召集的人数为起点的人数 33 } 34 35 int search(){ 36 int k = -1; 37 int mmin = Max; 38 for(int i = 0; i < n; i++){ 39 if(vis[i] == 0 && dist[i] < mmin){ //这个点没走过则选取一个最小的距离,确保每次选择的点是到达该点的最短的路径 40 k = i; 41 mmin = dist[i]; 42 } 43 } 44 //因为至少存在一条通路所以k的返回值不可能是-1 (若图是不连通的则某个时刻k会返回-1) 45 return k; 46 } 47 48 void run(){ 49 for(int i = 1; i <= n; i++){ //有n个点 则需要查询n-1次能走完所有的点 50 int k = search(); //查询最短距离的点 51 vis[k] = 1; //标记走过该点 52 for(int j = 0; j <= n-1; j++){ 53 if(vis[j] == 0 && dist[j] > mat[k][j] + dist[k]){ 54 dist[j] = mat[k][j] + dist[k]; 55 num[j] = num[k]; //如果通过k到达j的路径是最短的 则到达j的条数就是达到k的条数 56 man[j] = people[j] + man[k]; 57 } 58 //这里必须要用else if因为执行完上面的语句之后 dist[j] == mat[k][j] + dist[k]必定会成立则会导致再次执行下面的语句 59 else if(vis[j] == 0 && dist[j] == mat[k][j] + dist[k]){ 60 num[j] += num[k]; 61 man[j] = max(man[j], people[j] + man[k]); 62 } 63 } 64 } 65 printf("%d %d\n", num[t], man[t]); 66 } 67 68 int main(){ 69 while(scanf("%d%d%d%d", &n, &m, &s, &t) != EOF){ 70 init(); 71 run(); 72 } 73 return 0; 74 }