news 2026/7/28 7:17:43

Dijkstra算法实战:PTA紧急救援问题解析与优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dijkstra算法实战:PTA紧急救援问题解析与优化

1. 项目概述:PTA L2-001紧急救援问题解析

这道PTA题目是典型的带权图最短路径应用场景,要求使用Dijkstra算法解决城市紧急救援问题。题目会给出城市间的道路信息(边权)和每个城市的救援队伍数量(点权),需要找出从起点到终点的最短路径,并在多条最短路径中选择救援队伍最多的那条。

在实际工程中,这类算法广泛应用于导航系统、物流配送、网络路由等场景。比如救护车选择最优路线时,既要考虑路程最短,也要考虑能调配最多医疗资源的路径。

2. 核心算法解析:Dijkstra的实现要点

2.1 基础Dijkstra框架

标准Dijkstra算法使用优先队列(最小堆)实现,时间复杂度O(ElogV)。核心数据结构包括:

  • dist[]数组记录起点到各点的最短距离
  • visited[]数组标记已确定最短路径的点
  • 优先队列存储待处理的节点
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq; pq.push({0, start}); dist[start] = 0; while(!pq.empty()) { auto [d, u] = pq.top(); pq.pop(); if(visited[u]) continue; visited[u] = true; for(auto &[v, w] : graph[u]) { if(dist[v] > dist[u] + w) { dist[v] = dist[u] + w; pq.push({dist[v], v}); } } }

2.2 题目特殊要求的扩展

本题需要在标准Dijkstra基础上增加三个维度的信息:

  1. num[]记录到每个点的最短路径数量
  2. teams[]记录到每个点的最大救援队数量
  3. pre[]记录路径前驱节点用于最后输出路径

关键更新逻辑:

if(dist[v] > dist[u] + w) { dist[v] = dist[u] + w; num[v] = num[u]; teams[v] = teams[u] + rescue[v]; pre[v] = u; pq.push({dist[v], v}); } else if(dist[v] == dist[u] + w) { num[v] += num[u]; if(teams[u] + rescue[v] > teams[v]) { teams[v] = teams[u] + rescue[v]; pre[v] = u; } }

3. 完整代码实现与逐行解析

#include <iostream> #include <vector> #include <queue> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; void dijkstra(int n, int s, int d, vector<vector<pair<int,int>>>& graph, vector<int>& rescue, vector<int>& path) { vector<int> dist(n, INF); vector<int> num(n, 0); vector<int> teams(n, 0); vector<int> pre(n, -1); vector<bool> visited(n, false); dist[s] = 0; num[s] = 1; teams[s] = rescue[s]; priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq; pq.push({0, s}); while(!pq.empty()) { auto [dis, u] = pq.top(); pq.pop(); if(visited[u]) continue; visited[u] = true; for(auto &[v, w] : graph[u]) { if(dist[v] > dist[u] + w) { dist[v] = dist[u] + w; num[v] = num[u]; teams[v] = teams[u] + rescue[v]; pre[v] = u; pq.push({dist[v], v}); } else if(dist[v] == dist[u] + w) { num[v] += num[u]; if(teams[u] + rescue[v] > teams[v]) { teams[v] = teams[u] + rescue[v]; pre[v] = u; } } } } // 回溯路径 int cur = d; while(cur != -1) { path.push_back(cur); cur = pre[cur]; } reverse(path.begin(), path.end()); cout << num[d] << " " << teams[d] << endl; for(int i = 0; i < path.size(); ++i) { if(i != 0) cout << " "; cout << path[i]; } } int main() { int N, M, S, D; cin >> N >> M >> S >> D; vector<int> rescue(N); for(int i = 0; i < N; ++i) { cin >> rescue[i]; } vector<vector<pair<int,int>>> graph(N); for(int i = 0; i < M; ++i) { int u, v, w; cin >> u >> v >> w; graph[u].emplace_back(v, w); graph[v].emplace_back(u, w); } vector<int> path; dijkstra(N, S, D, graph, rescue, path); return 0; }

4. 关键难点与调试技巧

4.1 边界条件处理

  • 起点和终点相同的情况:需要特殊处理,此时路径数为1,救援队数量就是该城市的数量
  • 不可达情况:题目保证有解,实际工程中需要增加判断
  • 城市编号从0开始:注意题目输入要求,避免off-by-one错误

4.2 常见错误排查

  1. 优先队列使用错误

    • 错误做法:直接修改队列中的元素
    • 正确做法:将新状态重新push进队列,通过visited数组过滤旧状态
  2. 路径计数错误

    // 错误写法 num[v] = 1; // 正确写法 num[v] += num[u];
  3. 救援队累加错误

    // 错误写法(漏加当前城市救援队) teams[v] = teams[u]; // 正确写法 teams[v] = teams[u] + rescue[v];

4.3 性能优化建议

  1. 使用邻接表而非邻接矩阵存储稀疏图
  2. 优先队列使用pair时,将距离放在first元素(默认按first排序)
  3. 在找到终点后可提前终止算法(题目不要求时可以优化)

5. 算法扩展与变种思考

5.1 堆优化与斐波那契堆

当图规模极大时(如V>1e5),可以使用更高效的斐波那契堆实现,将时间复杂度降至O(E+VlogV)。不过C++标准库未提供,需要手动实现或使用第三方库。

5.2 A*算法的适用性

如果问题中能设计合理的启发式函数(如地理坐标间的直线距离),A*算法通常比Dijkstra更快找到终点。但在本题中由于缺少位置信息,Dijkstra是最佳选择。

5.3 动态图处理

实际场景中道路状况可能实时变化,可以考虑以下优化:

  1. 增量式Dijkstra:只重新计算受影响的部分路径
  2. 预处理技术:如Contraction Hierarchies等

6. 实际工程中的应用建议

  1. 内存优化:对于超大图,可以使用CSR(Compressed Sparse Row)格式存储邻接表
  2. 并行计算:使用多线程同时处理不同节点的松弛操作
  3. 持久化存储:预处理好的图结构可以序列化到磁盘,避免每次重新计算

在真实导航系统中,Dijkstra的变种算法通常需要处理:

  • 实时交通数据更新
  • 多维度权重(距离、时间、收费等)
  • 用户偏好设置(避开高速、优先步行等)

调试提示:在VS Code中调试时,可以使用以下launch.json配置观察变量:

{ "version": "0.2.0", "configurations": [ { "name": "C++ Debug", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}", "args": ["<", "input.txt"], "stopAtEntry": false, "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing", "text": "-enable-pretty-printing", "ignoreFailures": true } ] } ] }

最后分享一个实用技巧:在竞赛中遇到类似题目时,可以先将标准Dijkstra模板写出来,再根据题目要求逐步添加额外维度的信息处理,这样比一次性写完整代码更不容易出错。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/28 7:14:51

ESP32-S3与CircuitPython驱动OV2640构建网络摄像头全攻略

1. 项目概述&#xff1a;当ESP32-S3遇上CircuitPython与OV2640最近在捣鼓一个挺有意思的小玩意儿&#xff1a;用一块FireBeetle 2 ESP32-S3开发板&#xff0c;配上OV2640摄像头模组&#xff0c;再刷上CircuitPython固件&#xff0c;自己做了一个能联网的简易网络照相机。这听起…

作者头像 李华
网站建设 2026/7/28 7:14:05

深入理解JavaScript作用域链与常见问题解析

1. 为什么作用域链是JS开发者的必修课&#xff1f;刚接触JavaScript的前端开发者&#xff0c;往往会被各种"诡异"的变量访问问题困扰。比如下面这段代码&#xff1a;function outer() {var outerVar 我在外层;function inner() {console.log(outerVar); // 这里为什…

作者头像 李华
网站建设 2026/7/28 7:13:18

图卷积网络GCN终极指南:5分钟快速掌握图神经网络

图卷积网络GCN终极指南&#xff1a;5分钟快速掌握图神经网络 【免费下载链接】gcn Implementation of Graph Convolutional Networks in TensorFlow 项目地址: https://gitcode.com/gh_mirrors/gc/gcn 图卷积网络&#xff08;Graph Convolutional Networks&#xff0c;简…

作者头像 李华
网站建设 2026/7/28 7:12:06

DC-7靶场渗透:利用暴露的Drush命令重置Drupal管理员密码实战

1. 项目概述&#xff1a;一次从Web到Shell的“非典型”路径最近在复现DC-7这个经典的渗透测试靶场时&#xff0c;我发现了一个非常有意思的切入点。很多朋友拿到这个靶场&#xff0c;第一反应可能就是去扫目录、找后台、尝试SQL注入或者上传点。这当然没错&#xff0c;但DC-7的…

作者头像 李华
网站建设 2026/7/28 7:11:04

BQ27542-G1数据闪存访问、校验和与校准命令实战指南

1. 项目概述与核心价值在电池管理系统&#xff08;BMS&#xff09;和嵌入式设备开发中&#xff0c;我们经常会遇到一个核心需求&#xff1a;如何安全、可靠地配置和校准一颗“聪明”的电池燃料计&#xff08;Fuel Gauge&#xff09;。今天&#xff0c;我想以一个资深嵌入式工程…

作者头像 李华
网站建设 2026/7/28 7:08:08

AI对话系统中的状态跟踪设计与优化实践

1. AI原生应用中的对话状态跟踪模块设计在构建现代AI原生应用时&#xff0c;对话状态跟踪(DST)模块的质量直接决定了交互体验的流畅度。我经历过多个对话系统项目&#xff0c;发现约70%的用户流失都源于状态跟踪失效导致的对话断层。一个典型的电商客服场景中&#xff0c;当用户…

作者头像 李华