1003 Emergency【PAT (Advanced Level) Practice】

1003 Emergency【PAT (Advanced Level) Practice】

原题链接:预览题目详情 - 1003 Emergency (pintia.cn)

1.题目原文

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 N N ( ≤ 500 \le 500 500) - the number of cities (and the cities are numbered from 0 to N − 1 N-1 N1), M M M - the number of roads, C 1 C_1 C1 and C 2 C_2 C2 - the cities that you are currently in and that you must save, respectively. The next line contains N N N integers, where the i i i-th integer is the number of rescue teams in the i i i-th city. Then M M M lines follow, each describes a road with three integers c 1 c_1 c1, c 2 c_2 c2 and L L 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 C_1 C1 to C 2 C_2 C2.

Output Specification:

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

2. 题目翻译

作为一个城市的紧急救援队领队,你被提供了你国家的一张特殊地图。地图显示了一些通过一些道路连接的散落的城市。地图上标有每个城市中救援队的数量以及任意一对城市之间的道路长度。当你从其他城市接到紧急呼叫时,你的任务是尽快带领你的队伍前往该地点,并在此同时尽可能在途中召集更多的援助。

输入规范:

每个输入文件包含一个测试用例。对于每个测试用例,第一行包含4个正整数: N N N ≤ 500 \le 500 500)- 城市的数量(城市编号从0到 N − 1 N-1 N1), M M M - 道路数量, C 1 C_1 C1 C 2 C_2 C2 - 分别是你目前所在的城市和你必须拯救的城市。接下来一行包含 N N N个整数,其中第 i i i个整数是第 i i i个城市中的救援队数量。然后是 M M M行,每行描述一条道路,包含三个整数 c 1 c_1 c1 c 2 c_2 c2 L L L,分别是由一条道路连接的城市对以及该道路的长度。保证存在至少一条从 C 1 C_1 C1 C 2 C_2 C2的路径。

输出规范:

对于每个测试用例,在一行中打印两个数字: C 1 C_1 C1 C 2 C_2 C2之间的不同最短路径的数量,以及你可能收集到的最大数量的救援队。一行中的所有数字必须用一个空格分隔,行末不能有额外的空格。

示例输入:

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

示例输出:

2 4

3.解题思路

3.1题目分析

计算有多少条不同的最短路径能够到达目标城市,并在这些路径中,能够召集到的最大救援队数量是多少。

输入城市数量、道路数量、起始城市、目标城市,每个城市的救援队数量,以及城市之间道路的长度。

输出最短路径的数量和在这些路径上能够召集到的最大救援队数量。

3.2基本思路

使用Dijkstra算法来解决单源最短路径问题,其中目标是找到从起点到其他所有顶点的最短路径。在这个问题中,除了求最短路径数量外,还需要计算最短路径上经过的城市中,救援队数量的最大值。

3.3详解步骤

  1. 初始化: 初始化图的邻接矩阵和相关数组。设置起点到自身的最短路径为0,将所有其他顶点的最短路径长度设置为无穷大。同时,维护一个集合(通常用优先队列或最小堆实现)来存储未确定最短路径的顶点。

  2. 读取输入: 从输入中读取城市的数量(n)、道路的数量(m)、起点(s)、目标点(d),以及每个城市的救援队数量和道路的连接关系。

  3. Dijkstra算法: 使用Dijkstra算法计算从起点到目标点的最短路径数量以及最大救援队数量。在每一步中,选择当前路径最短的顶点,更新其邻接顶点的路径长度,并维护最短路径数量和最大救援队数量。

  4. 输出结果: 输出最短路径数量和最大救援队数量。

4.参考答案

#include <stdio.h>

#define MAXN 500
#define inf 1000000000//inf表示无穷大,即表示两城市之间无直接道路
typedef int elem_t;

//mat[MAXN][MAXN]: 二维数组,表示城市之间的道路长度的邻接矩阵。mat[i][j]存储了城市i到城市j之间的道路长度。
//min[MAXN]: 一维数组,表示从起点到每个城市的当前最短路径长度。min[i]存储了从起点到城市i的当前最短路径长度。
//pre[MAXN]: 一维数组,表示每个城市在最短路径中的前驱城市。pre[i]存储了在最短路径中城市i的前驱城市。
//store[MAXN]: 一维数组,表示每个城市的救援队数量。store[i]存储了城市i的救援队数量。

elem_t s, mat[MAXN][MAXN], min[MAXN], pre[MAXN], store[MAXN];

// 初始化图的邻接矩阵
void initialize(int n){
    
    
    int i, j;

    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            mat[i][j] = inf;//初始化所有城市距离无穷大,即无直接道路
}

// 读取城市的救援队信息和道路的长度
void read_store_matrix(int n, int m) {
    
    
    int i, j, k;

    // 初始化图的邻接矩阵
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            mat[i][j] = inf;

    // 读取每个城市的救援队数量
    for (i = 0; i < n; i++)
        scanf("%d", &store[i]);

    // 读取道路的连接关系和长度
    for (k = 0; k < m; k++) {
    
    
        scanf("%d %d", &i, &j);
        scanf("%d", &mat[i][j]);
        mat[j][i] = mat[i][j];  // 由于是无向图,道路是双向的,因此对称赋值
    }
}


// 使用Dijkstra算法计算最短路径和最大救援队数量
void dijkstra(int n, elem_t d){
    
    
    int v[MAXN], cnt[MAXN], jb[MAXN];
    int i, j, k;

    // 初始化数组
    for (i = 0; i < n; i++)
        min[i] = inf, v[i] = 0, cnt[i] = 0, jb[i] = store[i], pre[i] = -1;
    
    // 起点的路径长度为0,起点被标记为已确定最短路径
    cnt[s] = 1;

    for (min[s] = 0, j = 0; j < n; j++){
    
    
        // 选择当前路径最短的顶点
        for (k = -1, i = 0; i < n; i++)
            if (!v[i] && (k == -1 || min[i] < min[k]))
                k = i;

        // 标记当前顶点为已确定最短路径
        v[k] = 1;

        // 更新当前顶点的邻接顶点的路径长度
        for (i = 0; i < n; i++)
            if (!v[i]) {
    
    
                if (min[k] + mat[k][i] < min[i]) {
    
    
                    // 更新最短路径和最大救援队数量
                    min[i] = min[k] + mat[pre[i] = k][i];
                    cnt[i] = cnt[k];
                    jb[i] = jb[k] + store[i];
                }
                else if (min[k] + mat[k][i] == min[i]) {
    
    
                    // 如果有多条最短路径,累加路径数量和更新最大救援队数量
                    cnt[i] += cnt[k];
                    if (jb[k] + store[i] > jb[i]) 
                        jb[i] = jb[pre[i] = k] + store[i];
                }
            }
    }

    // 输出最短路径数量和最大救援队数量
    printf("%d %d\n", cnt[d], jb[d]);
}

int main(){
    
    
    int n, m;
    elem_t d;

    // 读取输入
    scanf("%d %d %d %d", &n, &m, &s, &d);
    
    // 初始化图和读取城市信息及道路长度
    initialize(n);
    read_store_matrix(n, m);

    // 运行Dijkstra算法
    dijkstra(n, d);

    return 0;
}

5.知识拓展

迪杰斯特拉算法(Dijkstra)

迪杰斯特拉算法(Dijkstra)是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

Dijkstra算法是一种用于求解单源最短路径问题的贪心算法。该算法通过逐步找到从起点到其他所有顶点的最短路径,具体步骤如下:

  1. 初始化: 将起点到自身的最短路径长度设置为0,将所有其他顶点的最短路径长度设置为无穷大。同时,维护一个集合(通常用优先队列或最小堆实现)来存储未确定最短路径的顶点。

  2. 选择顶点: 从未确定最短路径的顶点中选择一个,该顶点到起点的最短路径是已知的最小值。

  3. 更新路径: 对于选择的顶点,遍历其所有邻接顶点,更新起点到这些邻接顶点的路径长度。如果通过当前选择的顶点到达某邻接顶点的路径比已知的最短路径短,就更新最短路径和路径长度。

  4. 标记已确定: 将当前选择的顶点标记为已确定最短路径,从未确定最短路径的集合中移除。

  5. 重复: 重复步骤2-4,直到所有顶点都被标记为已确定最短路径,或者集合为空。

Dijkstra算法的特点是它对于权重非负的图非常有效。在每一步,它都选择当前路径最短的顶点,确保得到的路径是全局最短路径。然而,如果图中存在负权边,Dijkstra算法可能产生错误的结果,因此对于包含负权边的图,更适合使用Bellman-Ford算法。

贝尔曼-福特算法(Bellman-Ford)

贝尔曼-福特算法(Bellman-Ford)是一种用于求解单源最短路径问题的算法,与Dijkstra算法不同,Bellman-Ford算法可以处理带有负权边的图,缺点是时间复杂度过高。其基本思想是通过不断松弛边的权值,逐步逼近最短路径。具体步骤如下:

  1. 初始化: 将起点到自身的最短路径长度设置为0,将所有其他顶点的最短路径长度设置为无穷大。同时,初始化一个数组用于记录每个顶点的前驱顶点。

  2. 松弛边: 对于图中的每一条边,不断尝试通过当前路径是否能够获得更短的路径。如果可以,就更新最短路径长度和前驱顶点。

  3. 重复: 重复松弛边的过程,直到没有顶点的最短路径长度发生变化。

  4. 检测负权环: 如果在某一轮松弛操作中,仍然存在可以松弛的边,说明图中存在负权环路。因为负权环路可以不断降低路径长度,无法确定最短路径。

Bellman-Ford算法适用于包含负权边的图,但由于其时间复杂度为O(VE),其中V是顶点数量,E是边数量,相对于Dijkstra算法的O((V+E)logV)来说,性能相对较差。当图比较稀疏时,Dijkstra算法更为高效。

猜你喜欢

转载自blog.csdn.net/weixin_40171190/article/details/134745792
今日推荐