PAT 1003 Emergency (25分) Dijstra

Scroll Down

题目

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, C​1​​ and C​2​​ - 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 c​1​​ , c​2​​ 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 C​1​​ to C​2​​ .

Output Specification:
For each test case, print in one line two numbers: the number of different shortest paths between C​1​​ and C​2​​ , 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

题目解读

N 个城市 ,编号 从 1 - N-1,给出每个城市的救援队数目,给出若干城市之间之间的距离,现在某城市发生紧急事故,你要从当前所在的城市出发赶过去支援,并带上沿途所经过的所有城市的救援队。问:你有多少种到达目标城市的最短路径,以及在这些路径中,你所能带过去最多的救援队的数目

思路分析

  • 首先,最短路径肯定要有Dijstra,但现在不光要保存起点到终点的最短路径,还要保存起点到最短路径的数目,以及能带过去的最多的路径数。所以需要三个数组
	// 起点到其他城市之间的最短距离 
	int mindis[501];
	fill(mindis, mindis + 501, INT_MAX);
	// 起点到其他城市的最短路线数目
	int mindisNum[501] = {0}; 
	// 起点到达其他城市的所有最短路径中,能够带上的最多的救援队 
	int maxTeams[501] = {0};
  • Dijstra起点的初始化赋值
	// 初始化 
	mindis[start] = 0;
	// 到起点只有一种方式 
	mindisNum[start] = 1;
	// 到起点只能带当地的治疗队 
	maxTeams[start] = teams[start];
  • Dijstra算法更新最短路径的步骤我们很熟悉了,我们只需要在每次更新距离的同时,加上对mindisNummaxTeams数组的更新即可。
	// 更新最短路径的条件
	if (!visited[j] && dis[k][j] != INT_MAX) {
			// d[j] > d[k] + w[k][j]
			if (mindis[j] > mindis[k] + dis[k][j]) {
				// 更新到j的最短距离 
				mindis[j] = mindis[k] + dis[k][j];
				// 到j的最短路径数等于到k的最短路径数 
				mindisNum[j] = mindisNum[k];
				// 到达j时能带的最多的救援队的数目 = 到达k时能带的最多的救援队数目 + j当地的救援队数目 
				maxTeams[j] = maxTeams[k] + teams[j]; 
			// 说明到达j又多了一种方式 
			} else if (mindis[j] == mindis[k] + dis[k][j]) {
				// 注意次数的更新条件是 += ,因为是在原来的基础上多出来的 
				mindisNum[j] += mindisNum[k];
				// 这条新的线路能带的医疗队数目是不是比之前线路的更多 
				if (maxTeams[k] + teams[j] > maxTeams[j])
					 maxTeams[j] = maxTeams[k] + teams[j];
			}
		}
  • 最终要输出的就是 printf("%d %d", mindisNum[end], maxTeams[end]);

完整代码

#include <iostream>
#include <algorithm>
#include <climits> 
using namespace std;

int main() {
	// 多少城市,给出几条路距离 
	int cities, roads;
	// 起点,终点 
	int start, end;
	// 每个城市的救援队数目 
	int teams[501] = {0};
	// 任意两点间的距离 
	int dis[501][501]; 
	fill(dis[0], dis[0] + 501 * 501, INT_MAX);
	// 起点到其他城市之间的最短距离 
	int mindis[501];
	fill(mindis, mindis + 501, INT_MAX);
	// 起点到其他城市的最短路线数目
	int mindisNum[501] = {0}; 
	// 起点到达其他城市的所有最短路径中,能够带上的最多的救援队 
	int maxTeams[501] = {0};
	// 第一行输入 
	cin >> cities >> roads >> start >> end;
	// 第二行输入 
	for (int i = 0; i < cities; ++i) {
		cin >> teams[i];
	}
	// 剩下的每一行是路距离
	int from, to, dist; 
	for (int i = 0; i < roads; ++i) {
		cin >> from >> to >> dist;
		dis[to][from] = dis[from][to] = dist; 
	}
	// 最短路径
	bool visited[501] = {false};
	// 求出从起点开始,到其他节点的最短距离 
	
	// 初始化 
	mindis[start] = 0;
	// 到起点只有一种方式 
	mindisNum[start] = 1;
	// 到起点只能带当地的治疗队 
	maxTeams[start] = teams[start];
	 
	int k, min_dis;
	
	// Dijstra
	for (int i = 0; i < cities; ++i) {
		
		k = -1; min_dis = INT_MAX;
		// 找出下一个要加入的节点 
		for (int j = 0; j < cities; ++j) {
			if (!visited[j] && mindis[j] < min_dis) {
				min_dis = mindis[j];
				k = j;
			}
		}
		if (k == -1) break;
		
		visited[k] = true;
		for (int j = 0; j < cities; ++j) {
			if (!visited[j] && dis[k][j] != INT_MAX) {
				// d[j] > d[k] + w[k][j]
				if (mindis[j] > mindis[k] + dis[k][j]) {
					// 更新到j的最短距离 
					mindis[j] = mindis[k] + dis[k][j];
					// 到j的最短路径数等于到k的最短路径数 
					mindisNum[j] = mindisNum[k];
					// 到达j时能带的最多的救援队的数目 = 到达k时能带的最多的救援队数目 + j当地的救援队数目 
					maxTeams[j] = maxTeams[k] + teams[j]; 
				// 说明到达j又多了一种方式 
				} else if (mindis[j] == mindis[k] + dis[k][j]) {
					// 注意次数的更新条件是 += ,因为是在原来的基础上多出来的 
					mindisNum[j] += mindisNum[k];
					// 这条新的线路能带的医疗队数目是不是比之前线路的更多 
					if (maxTeams[k] + teams[j] > maxTeams[j])
						 maxTeams[j] = maxTeams[k] + teams[j];
				}
			}
		}
	}
	// 输出到终点的最短路径数目+能够带的最多医疗队数目 
	printf("%d %d", mindisNum[end], maxTeams[end]);

    return 0;
}