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个结点

      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])

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

代码

// 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/

欢迎讨论和交流!


原文地址:https://www.cnblogs.com/chouxianyu/p/13686461.html