当前位置: 首页 > news >正文

题解:P4779 【模板】单源最短路径(标准版)

题目传送门

算法分析

本题要求计算单源最短路径,并且边权非负,适合使用Dijkstra 算法。Dijkstra 算法是一种贪心算法,用于计算带权有向图或无向图中单个源节点到所有其他节点的最短路径。

为什么选择 Dijkstra 算法?

  1. Dijkstra 算法要求所有边权非负。在本题中,题目明确说明边权 \(0 ≤ wi ≤ 10^9\),完全符合 Dijkstra 的应用条件。若存在负权边,Dijkstra 可能会得到错误结果,此时需使用 Bellman-Ford 或 SPFA 算法。

  2. 朴素实现:\(O (n²)\),适用于节点数较少的稠密图
    优先队列优化:\(O ((m+n) logn)\),其中m为边数,n为节点数。本题数据范围为\(1 ≤ n ≤ 10^5\)\(1 ≤ m ≤ 2×10^5\),使用优先队列优化后的 Dijkstra 可以高效处理。

  3. Dijkstra 算法是确定性算法,对于相同的输入,结果唯一且可预测。相比之下,某些近似算法或启发式算法(如 A*)可能需要额外信息(如启发函数),而 Dijkstra 仅依赖图的结构和边权。

  4. 本题要求计算从起点s到所有其他节点的最短路径,Dijkstra 算法正是为此场景设计。若仅需计算特定终点的最短路径,可在找到终点后提前终止算法。

本题为何不选择其他算法?

Floyd-Warshall 算法:时间复杂度 \(O (n³)\),适用于多源最短路径,但本题仅需求单源路径,使用该算法会导致超时。
Bellman-Ford/SPFA 算法:适用于含负权边的图,时间复杂度较高\((O (mn))\),在本题的非负权图中效率低于 Dijkstra。

注意事项

由于边权非负,Dijkstra 算法可以正确处理本题。
使用优先队列时,由于 C++ 的优先队列默认是最大堆,因此存储距离的负值来模拟最小堆。
初始化距离数组时,使用 \(2147483647\) 表示无穷大,避免溢出。

算法思路

  1. 初始化所有节点的距离为无穷大,起点的距离为\(0\)
  2. 使用优先队列(最小堆)来维护待处理的节点,优先处理距离最小的节点。
  3. 每次从优先队列中取出距离最小的节点,遍历其所有邻接边,更新邻接节点的距离。
  4. 重复步骤3,直到优先队列为空。

代码实现

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<ext/pb_ds/priority_queue.hpp>
#define MP(x, y) make_pair(x, y)
#define Pair pair<int, int> 
using namespace std;
const int MAXN = 1e6 + 10, INF = 2147483646, B = 19;// 快速读入函数
inline int read() {char c = getchar(); int x = 0, f = 1;while(c < '0' || c > '9') {if(c == '-') f = 1; c = getchar();}while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();return x * f;
}int N, M, S;// 边的结构体
struct Edge {int u, v, w, nxt;
};
Edge E[MAXN];
int head[MAXN], num = 1;// 添加边
void AddEdge(int x, int y, int z) {E[num] = (Edge) {x, y, z, head[x]};head[x] = num++;
}int dis[MAXN], vis[MAXN];
priority_queue<Pair> q; // Dijkstra算法实现
void Dij() {// 初始化距离数组for(int i = 1; i <= N; i++) dis[i] = 2147483647;dis[S] = 0; q.push(MP(0, S));while(!q.empty()) {int p = q.top().second; q.pop();if(vis[p]) continue; vis[p] = 1;// 遍历当前节点的所有邻接边for(int i = head[p]; i != -1; i = E[i].nxt) {int to = E[i].v;// 松弛操作if(dis[to] > dis[p] + E[i].w) dis[to] = dis[p] + E[i].w,q.push(MP(-dis[to], to));}}// 输出结果for(int i = 1; i <= N; i++)printf("%d ", dis[i]);
}int main() {// 初始化头数组memset(head, -1, sizeof(head));// 读入数据N = read(); M = read(); S = read();for(int i = 1; i <= M; i++) {int x = read(), y = read(), z = read();AddEdge(x, y, z);}// 执行Dijkstra算法Dij();return 0;
}

代码解释

· 快速读入函数:由于输入数据量较大,使用快速读入函数可以提高效率。

· 邻接表存储图:使用结构体数组和头数组来存储图的邻接表,便于遍历每个节点的所有邻接边。

· Dijkstra 算法:使用优先队列优化的 Dijkstra 算法,每次取出距离最小的节点进行处理,并更新其邻接节点的距离。

· 松弛操作:对于每个邻接节点,如果通过当前节点到达该节点的距离更短,则更新该节点的距离。

复杂度分析

时间复杂度:\(O ((m + n) logn)\),其中 n 是节点数,m 是边数。

空间复杂度:\(O (m + n)\),主要用于存储图的邻接表和优先队列。

审核大大求过

http://www.hskmm.com/?act=detail&tid=25960

相关文章:

  • 网关配置
  • 高频感应钎焊在制冷行业的应用与优势:高效、绿色、智能的焊接革命!
  • 题解:P12672 「LAOI-8」近期我们注意到有网站混淆视听
  • 详细介绍:基于LangChain构建高效RAG问答系统:向量检索与LLM集成实战
  • EPU+VPU+WBUC+WAUC:AI元人文的硅基基石体系
  • 股市技术分析突破
  • 干货分享:无需下载,在线快速编辑图片的完整教程
  • 34.1STM32下的can总线实现知识(区分linux)_csdn - 详解
  • js实现promise常用场景使用示例
  • 英语_阅读_Balancing Benefits and Risks_待读
  • 大模型部署
  • 读技术之外:社会联结中的人工智能02劳工
  • linux
  • 鼠标图标更改样式
  • webpack和vite的区别 - 指南
  • m3u8在线播放测试的方法与常见问题解决方案(附网页演示
  • 校招题
  • Manim实现旋转扭曲特效
  • go语言学习 第5章:函数 - 详解
  • 混沌熵池:“创造之源”还是“皇帝的新衣”?
  • 间谍软件通过虚假自然灾害警报传播
  • KaTeX手册
  • Qt编写上下界面切换效果/前进到下一个界面/后退到上一个页面/零件工艺及管理设计系统
  • 【题解】P1131 [ZJOI2007] 时态同步
  • LGP9120 [NOIP 2022.5] 密码锁 学习笔记
  • 深入解析:OpenCV CUDA模块图像处理------创建CUDA加速的Canny边缘检测器对象createCannyEdgeDetector()
  • 机器人技术奖学金项目助力STEM教育发展
  • SAP ABAP 事务码 RZ12 里的 Max Number of WPs Used 参数的作用介绍
  • busybox 没有 clear 命令吗
  • 实用指南:Hive SQL 中 BY 系列关键字全解析:从排序、分发到分组的核心用法