PTA 1003 Emergency
题目翻译
作为一个城市的紧急救助队的队长的你有一份特殊的你所在国家的地图。地图展示了一些通过道路连接的分散的城市。每一个城市都有急救队并且地图上标出了一些城市之间道路的长度。当你接到一个从其他城市打来的电话时,你的任务是带领你的队员尽快到达那里同时叫上尽可能多的帮手。
输入格式
每一个输入文件包含一个测试样例。对于每一个测试样例,第一行有4个正整数:N(≤500)-城市的数量(城市编号从0到N-1),M-道路的数量,C1和C2-分别为你现在所在的城市和你必须要去救援的城市。下一行包含N个整数--分别是第i个城市所拥有的救援队的数量。接下来的M行分别用三个整数C1和C2和L表示一对城市和他们之间道路的长度。C1 和C2之间保证至少有一条通路。
输出格式
对于每一个测试样例,输出两个数字在一行:C1和C2之间的最短路径的条数以及你最多可以集合的救援队的数量。一行中的所有数字都必须用一个空格隔开并且行尾没有多余的空格。
把自己的一些想法和《算法笔记》里对Dijstra的介绍结合了一下(其实主要是算法笔记里的内容orz),希望能帮助大家理解这道题吧。小白第一次写博客,如果有错误请大家指出,感谢。
题目分析:
读完题目后,我们可以确定这是一个单源最短路的问题,即求解从C1到C2的最短路径,如果有多个的话就输出路径的个数,并且求出这些路径中能够集结的救援队个数最多的路。对于非负权的单源最短路问题,我们一般用Dijstra算法解决。
Dijstra算法基本策略:
设置集合S存放已被访问的定点(即已访问过的城市),然后执行n次下面的两个步骤(n为顶点个数):
- 每次从集合V-S(即未访问过的城市)中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S(即标记为已访问)。
- 之后,以顶点u为中介,优化与u连通的其他城市吗,如果借助顶点u能够使他们到达起点s的距离更短的话,就更新他们与s之间的最短距离。
- 重复回到1
具体实现时的一些细节
- 涉及到图的问题我们一般用邻接矩阵或者邻接表来存储图,这道题的城市,也就是节点,的个数最多是510,在我们接受范围以内,所以我推荐用邻接矩阵来存储图。这样在编写代码时相较于邻接表会方便一点。
- 在实现Dijstra算法时,我们一般用一个bool类型的一维数组来标记各个顶点是否被访问,我们暂且用vis来命名这个数组。一开始,我们先令vis[s]=true,也就是先把起点标记为已访问。之后每访问一个节点,都把它标记为true。
- 我们一般用一个int型的数组来存放各个顶点到起点的最短距离,我们一开始先把这个数组中除了起点以外都初始化为一个很大的数字,可以是或者是0xfffffff,但最好不要使用0x7fffffff,防止两个数相加发生溢出。
核心算法伪代码实现
1 void Dijstra(G, d[], s) 2 { 3 初始化 4 for (循环n次) { 5 u = 使d[u]最小的还未被访问的顶点的标号; 6 标记u为已访问 7 for (从u出发能到达的所有顶点v) { 8 if (v未被访问 && 以u为中介点使s到顶点v的最短距离d[v]更优) { 9 优化d[v]; 10 } 11 } 12 } 13 14 }
完整代码
1 #include <stdio.h> 2 #include <algorithm> 3 using namespace std; 4 const int MAXV = 510; 5 const int INF = 100000000; 6 7 int n, m, s, ed, G[MAXV][MAXV], weight[MAXV]; 8 int d[MAXV], w[MAXV], num[MAXV]; 9 bool vis[MAXV]; 10 11 void Dijkstra(); 12 13 int main() 14 { 15 scanf("%d%d%d%d", &n, &m, &s, &ed); 16 17 for (int i = 0; i < n; i++) 18 scanf("%d", &weight[i]); 19 20 fill(G[0], G[0] + MAXV * MAXV, INF); 21 for (int i = 0; i < m; i++) 22 { 23 int u, v; 24 scanf("%d%d", &u, &v); 25 scanf("%d", &G[u][v]); 26 G[v][u] = G[u][v]; 27 } 28 29 Dijkstra(); 30 31 printf("%d %d", num[ed], w[ed]); 32 return 0; 33 } 34 35 void Dijkstra() 36 { 37 fill(d, d + MAXV, INF); 38 fill(num, num + MAXV, 0); 39 fill(w, w + MAXV, 0); 40 fill(vis, vis + MAXV, false); 41 d[s] = 0; 42 w[s] = weight[s]; 43 num[s] = 1; 44 45 for (int i = 0; i < n; i++) 46 { 47 int u = -1, MIN = INF; 48 for (int j = 0; j < n; j++) 49 if (!vis[j] && d[j] < MIN) 50 { 51 u = j; 52 MIN = d[j]; 53 } 54 if (u == -1) 55 return; 56 vis[u] = true; 57 58 for (int j = 0; j < n; j++) 59 if (G[u][j] != INF && !vis[j]) 60 if (d[j] > G[u][j] + d[u]) 61 { 62 d[j] = G[u][j] + d[u]; 63 w[j] = w[u] + weight[j]; 64 num[j] = num[u]; 65 } 66 else if (d[j] == G[u][j] + d[u]) 67 { 68 w[j] = max(w[j], w[u] + weight[j]); 69 num[j] += num[u]; 70 } 71 } 72 }