PAT甲级1003Emergency

题目链接

https://pintia.cn/problem-sets/994805342720868352/problems/994805523835109376

题解

题目要求

  • 有几个城市,每个城市中有多个救援队,城市间由公路相连,公路的长度不一

  • 输入

    • N:正整数,不超过500,城市的数量,城市索引为[0,N-1]

    • M:公路的数量

    • c1:你现在所在的城市

    • c2:你需要去的城市

    • 各城市中救援队数量:N个整数

    • 各条公路连接的两个城市其该公路的长度:M行

      保证c1到c2至少有一条路径

  • 输出

    • c1到c2的最短路径的数量
    • 在保证路径最短的前提下,最多可以聚集多少只救援队

解题思路

  • dijkstra算法

    • 特点

      • 适用于边权为正的情况
      • 单源最短路(Single-Source Shortest Paths,SSSP),求单个源点到所有结点的最短路
      • 同时适用于有向图和无向图
    • 伪代码

      n个结点

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      bool visited[n];  // 标记:是否找到源点s到某点的最短路径
      int dis[n]; // 记录源点s到某点的最短路径的长度
      int w[n][n]; // 结点间的距离

      visited数组置false;
      dis数组置无穷大,dis[s]置0;
      循环n次{
      寻找未被标记的、距离结点s最近的结点u;
      如果找到u则将其标记(visited[u] = true),否则结束循环;
      如果存在边<u,v>,则更新dis[v] = min(dis[v], dis[u] + w[u][v]); // 贪心
      }
  • 其它

    • c1到c2的最短路径的数量pathNum

      • 如果dis[v] < dis[u] + w[u][v],则pathNum[v] = path[u]

        u到v只有一条边,所以源点到u和v的路径的数量相等

      • 如果dis[v] == dis[u] + w[u][v],则pathNum[v] = path[u] + path[v]

        在距离相等的情况下,除了经过u,还可以从其他结点到达v

    • 在保证路径最短的前提下,最多可以聚集多少只救援队

      • 如果dis[v] < dis[u] + w[u][v],则teamGatherNum[v] = teamGatherNum[u] + teamNum[v]

      • 如果dis[v] == dis[u] + w[u][v],则teamGatherNum[v] = min(teamGatherNum[v], teamGatherNum[u] + teamNum[v])

        此时最短路径不止一条,所以要判断哪条路径聚集的救援队更多

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Problem: PAT Advanced 1003
// URL: https://pintia.cn/problem-sets/994805342720868352/problems/994805523835109376
// Tags: 最短路 djikstra DFS 单源最短路 贪心

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

const int MAXN = 500; // 最多500个城市
const int INF = INT_MAX; // 最大距离
int cityDis[MAXN][MAXN]; // 城市间的距离
int teamNum[MAXN]; // 每个城市中有多少个救援队
int shortestPathDis[MAXN]; // 起点城市c1到各个城市最短路径的距离
int shortestPathNum[MAXN]; // 起点城市c1到各个城市最短路径的数量
int teamGatherNum[MAXN]; // 从起点城市c1到各个城市的最短路径上,最多能聚集到多少个救援队
bool visited[MAXN]; // 标记:是否已求出起点城市c1到某城市的最短路径

int main()
{
// 初始化变量
fill(cityDis[0], cityDis[0] + MAXN * MAXN, INF);
fill(shortestPathDis, shortestPathDis + MAXN, INF);

// 读取输入
int n, m, c1, c2;
scanf("%d %d %d %d", &n, &m, &c1, &c2);
for (int i = 0; i < n; i++)
scanf("%d", &teamNum[i]);
int a, b, d;
for (int i = 0; i < m; i++){
scanf("%d %d %d", &a, &b, &d);
cityDis[a][b] = cityDis[b][a] = d;
}

// 初始化起点城市c1
shortestPathDis[c1] = 0;
teamGatherNum[c1] = teamNum[c1];
shortestPathNum[c1] = 1;

// 迭代直至求出起点城市c1到所有城市的最短距离
for (int i = 0; i < n; i++){
// 寻找未被标记的、距离起点城市c1最近的城市u(贪心)
int u = -1, minDis = INF;
for(int j = 0; j < n; j++){
if (!visited[j] && shortestPathDis[j] < minDis){
u = j;
minDis = shortestPathDis[j];
}
}
// 各城市都已被标记,则已求出结果,退出循环即可;否则根据贪心策略,现在已求出起点城市c1到城市u的最短距离
if (u == -1)
break;
visited[u] = true;
// 如果有边<u,v>,则更新城市v的相关变量(最简洁的dijkstra算法只更新shortestPathDis[v]即可)
for (int v = 0; v < n; v++){
if (!visited[v] && cityDis[u][v] != INF){
if (shortestPathDis[v] > shortestPathDis[u] + cityDis[u][v]){
shortestPathDis[v] = shortestPathDis[u] + cityDis[u][v];
shortestPathNum[v] = shortestPathNum[u];
teamGatherNum[v] = teamGatherNum[u] + teamNum[v];
}
else if (shortestPathDis[v] == shortestPathDis[u] + cityDis[u][v]){
shortestPathNum[v] = shortestPathNum[u] + shortestPathNum[v];
if (teamGatherNum[v] < teamGatherNum[u] + teamNum[v])
teamGatherNum[v] = teamGatherNum[u] + teamNum[v];
}
}
}
}
printf("%d %d", shortestPathNum[c2], teamGatherNum[c2]);
return 0;
}

参考链接

https://blog.csdn.net/liuchuo/article/details/52300668

https://blog.csdn.net/qq_35644234/article/details/60870719


作者:@臭咸鱼

转载请注明出处:https://www.cnblogs.com/chouxianyu/

欢迎讨论和交流!