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

题目内容:

解题思路:

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

dijkstra算法:

        Dijkstra算法的核心思想是贪心策略,它逐步扩展最短路径树,直到包含所有顶点。算法维护一个集合,该集合包含从源点到集合中每个顶点的最短路径。

        首先针对本题输入样例,对于每个点需要记录三个数据,是否走过到达该点的最短距离父节点。

        步骤一、选择目前所能到达的最短距离的点,记录该点已经走过。

        步骤二、搜寻该点所能到达的所有点,比较新距离和原距离,若新距离更小则更新距离,以及父节点。

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

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

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

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值