PTA题解 L2-001 紧急救援(C++)

紧急救援

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2

输出样例:

2 60
0 1 3

解题思路:

本题整体是一个运用 dijkstra算法 的题目,然后在传统dijkstra算法里增加了 最多救援队最短路径的条数 的要求,接下来我会根据本题简单讲解一下 dijkstra算法 和在这基础上如何找到 在路径最短的基础上救援队最多 的路径,以及如何寻找 最短路径的条数。

Dijkstra 算法简介

Dijkstra 算法是一种经典的贪心算法,其核心思想是通过逐步扩展最短路径树,来找到从源点到图中其他所有顶点的最短路径。算法维护一个集合,集合内包含从源点到每个顶点的最短路径。

针对本题,在处理每个点时,需要记录以下三个数据:

  1. 是否走过:用于标记该点是否已被访问,防止重复计算。
  2. 到达该点的最短距离:记录从源点到该点的最短路径长度。
  3. 父节点:用于回溯路径,找到从源点到目标点的具体路径。

算法步骤如下:

  1. 步骤一:在所有未访问的点中,选择当前所能到达的最短距离的点,并记录该点已被访问。
  2. 步骤二:搜寻该点所能到达的所有未访问点,计算新的距离(即当前点的最短距离加上当前点到目标点的边的权重)。若新距离小于原距离,则更新距离和父节点;若新距离等于原距离,则根据救援队数量来决定是否更新父节点。
  3.  

            题目样例过于简单,可能并不能看出 Dijkstra算法 的操作步骤,下面我展示一个稍微复杂一点的案例。

    故可以得到最短路径为 1 --> 3 --> 6 --> 8 --> 9, 最短路径长度为24。

            然后对于本题还需要添加两个参数,救援队人数,这个参数可以和距离同时进行更新;第二个参数是 最短路径的条数,可以定义一个标记量,在遇到距离相同时,进行更新,同时比较救援队人数选择它的父节点。

针对本题的扩展

  1. 最多救援队数量:定义一个数组st,用于存储到达每个节点时能够召集的最多救援队数量。在更新距离的同时,更新该数组。例如,当找到一条更短路径到达某个节点时,相应地更新该节点的最大救援队数量;若距离相同,则比较当前路径和新路径的救援队数量,选择数量更多的路径更新。
  2. 最短路径的条数:定义一个数组num,用于存储到达每个节点的最短路径的条数。初始时,源点的路径条数为 1。当发现一条新的最短路径到达某个节点时,将该节点的路径条数更新为当前节点的路径条数;若新路径的距离与原路径相同,则将该节点的路径条数增加当前节点的路径条数。

C++代码展示:

#include <bits/stdc++.h>
using namespace std;

const int N = 505; // 定义最大节点数
const int INF = 1000000; // 定义无穷大

int n, m, s, d; // 定义变量n(节点数),m(边数),s(起点),d(终点)
int saveteam[N], st[N]; // 定义数组saveteam用于存储每个节点的救援队人数,st用于存储到达每个节点的最多救援队人数
int mm[N][N]; // 定义邻接矩阵mm,用于存储图的权重
int vis[N] = {0}; // 定义数组vis,用于标记节点是否已经被访问过
int dis[N]; // 定义数组dis,用于存储从起点到每个节点的最短距离
int father[N]; // 定义数组father,用于存储路径树,即每个节点的父节点
int num[N] = {0}; // 定义数组num,用于存储到达每个节点的路径数量

void Dijkstra() {
    fill(dis, dis + N, INF); // 初始化所有节点的距离为无穷大
    
    dis[s] = 0; // 起点到自身的距离为0
    num[s] = 1; // 起点的路径数量为1
    
    for (int i = 0; i < n; i++) {
        int node = -1, minn = INF; // 定义变量node用于存储当前最近的节点,minn用于存储最小距离
        for (int j = 0; j < n; j++) {
            if (vis[j] == 0 && minn > dis[j]) {
                node = j; // 更新最近的节点
                minn = dis[j]; // 更新最小距离
            }
        }
        
        vis[node] = 1; // 标记当前节点为已访问
        
        if (node == -1) return; // 如果没有找到未访问的节点,则退出
        
        for (int j = 0; j < n; j++) {
            if (vis[j] == 0) { // 遍历未访问的节点
                if (mm[node][j] != -1 && dis[node] + mm[node][j] < dis[j]) {
                    father[j] = node; // 更新父节点
                    dis[j] = dis[node] + mm[node][j]; // 更新距离
                    st[j] = st[node] + saveteam[j]; // 更新到达该节点的最多救援队人数
                    num[j] = num[node]; // 更新路径数量
                } else if (mm[node][j] != -1 && dis[node] + mm[node][j] == dis[j]) {
                    if (st[j] < st[node] + saveteam[j]) {
                        father[j] = node; // 更新父节点
                        st[j] = st[node] + saveteam[j]; // 更新到达该节点的最多救援队人数
                    }
                    num[j] += num[node]; // 更新路径数量
                }
            }
        }
    }
}

int main() {
    vector<int> sc; // 定义一个向量,用于存储路径
    cin >> n >> m >> s >> d; // 读取节点数、边数、起点和终点
    for (int i = 0; i < n; i++) {
        cin >> saveteam[i];
        father[i] = i; // 初始化每个节点的父节点为自己
        st[i] = saveteam[i]; // 初始化到达每个节点的救援队人数
    }
    
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            mm[i][j] = -1; // 初始化邻接矩阵
        }
    }
    
    for (int i = 0; i < m; i++) {
        int a, b, l;
        cin >> a >> b >> l;
        mm[a][b] = l; // 更新邻接矩阵
        mm[b][a] = l; // 无向图,所以需要更新两个方向的权重
    }
    
    Dijkstra(); // 调用Dijkstra算法
    
    cout << num[d] << " " << st[d] << "\n"; // 输出到达终点的路径数量和最多救援队人数
    
    int p = d; // 定义变量p,用于存储当前节点
    while (1) {
        if (p == father[p]) {
            sc.push_back(p); // 将当前节点加入路径
            break; // 如果当前节点是起点,则退出循环
        }
        sc.push_back(p); // 将当前节点加入路径
        p = father[p]; // 向上追溯父节点
    }
    
    for (int i = sc.size() - 1; i > 0; i--) { // 逆序输出路径
        cout << sc[i] << " ";
    }
    cout << sc[0] << "\n";
    
    return 0;
}

### 回答1: 作为城市应急救援队伍的负责人,我有一张特殊的全国地图。地图上标注了多个分散的城市和连接城市的快速道路,每个城市的救援队数量和每条道路的长度也都有标注。当其他城市有紧急求助电话时,我的任务是带领救援队尽快赶往事发地,并在路上尽可能多地召集其他救援队。 ### 回答2: 作为城市应急救援队伍的负责人,在接到紧急求助电话后,首先需要用目光扫过地图,根据事发地与各城市之间的距离,计算出最短路程。然后,根据每个城市现有的救援队伍数量,评估出最优的前往方式。 如果需要从起点城市前往目的地,并且途中有多个城市,则需要派遣救援队员在途中的城市中集结,以便在到达目的地时有更多的救援队员参与救援行动。在派遣救援队员的过程中,除了基于距离和数量因素考虑外,还需要考虑人员相互合作的能力和沟通的效率,以保证救援行动的协调性和高效性。 在实际救援中,应急救援队伍需要具备很高的整合能力和分析解决问题的能力,需要与各方面科室和组织进行紧密合作,确保事故和灾难得到快速、高效的解决。在做好救援工作的同时,应急救援队伍多要关注安全与保障,切实保护人员生命和财产。 总之,城市应急救援队伍作为保障城市公共安全的关键力量,需要团结协作、高效应对各种意外事件,保障公共和私人人身财产安全。 ### 回答3: 作为一个城市的应急救援队伍的负责人,拥有这张特殊的全国地图,对我们的救援行动将有很大的帮助。我们需要充分利用地图上标示的每个城市的救援队数量和每一条连接两个城市的快速道路长度来快速响应和有效救援。 当其他城市有紧急求助电话给我们时,我们需要迅速反应,查看地图上该城市所在位置和与其相邻的城市和道路连接情况。然后,我们会根据救援队数量和距离来决定调派哪些救援队前往事发地。同时,为了减少响应时间并召集更多的救援队,我们会在救援队数量较少的城市中寻找志愿者和爱心人士协助我们的行动,以尽可能地扩大救援队规模。 在整个救援行动过程中,我们需要密切关注路况和交通情况,考虑到我们有可能跨越多个城市前往救援,因此选择最佳的道路和路线很关键。我们还需要与其他应急救援队伍保持通讯和联动,协调各方救援资源,确保快速、高效地响应和处置紧急情况。 此外,除了切实有效地响应和处置紧急情况,我们也需要重视应急救援工作的预设和整体规划,定期组织应急演练和建立应急预案库,以提高应对突发事件的应变能力和救援效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值