题解:P4779 【模板】单源最短路径(标准版)
Solution
update on 2025.8.12:@ED_Builder Solusion->Solution。
思路
使用博客园效果更佳。
首先,题目说的很明白,这个广为人知的最短路算法就是 SPFA,它死了。这题会专门卡 SPFA,别问我咋知道的,在最劣情况下 SPFA 时间复杂度
这题和简单版的唯一区别就是简单版可以
dijkstra 的实现过程:
用一个 queue 维护我们可以走到的点,dis[u] 表示从起点走到 queue 中。
我们可以举个例子:
| 1 | 2 | 3 | 4 |
|---|---|---|---|
| queue | 1 | ||
| dis | 0 |
初始时没有任何操作。
然后从起点遍历,点
| 1 | 2 | 3 | 4 |
|---|---|---|---|
| queue | 2 | ||
| dis | 0 | 2 |
遍历到了点
| 1 | 2 | 3 | 4 |
|---|---|---|---|
| queue | 2 | 3 | |
| dis | 0 | 2 | 3 |
然后点
从节点
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| queue | 3 | 4 | ||
| dis | 0 | 2 | 3 | 6 |
点
从节点
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| queue | 4 | |||
| dis | 0 | 2 | 3 | 4 |
然后把点
队列空了,结束。
最终结果:
| 1 | 2 | 3 | 4 | |
|---|---|---|---|---|
| queue | ||||
| dis | 0 | 2 | 3 | 4 |
但是为什么不能处理负边权?
因为 dijkstra 基于贪心思想,遍历到一次的节点不会再次更新。
上面的过程是 dijkstra 的朴素做法,复杂度
由于 dijkstra 是基于贪心的算法,因此我们可以用 priority_queue 优化。
注意事项:
- 由于我们
dis 要取\min 值,所以dis 初始化为一个极大值,通常为\inf 。 - 题目是有向边。
代码:
void dijkstra(int s)
{
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
memset(dis, 0x3f, sizeof(dis)); // 初始化
dis[s] = 0;
q.push({dis[s], s}); // 将起点加入队列
while (!q.empty())
{
int u = q.top().second;
q.pop();
if (vis[u]) // 若 u 已访问,跳过
continue;
vis[u] = true; // 标记为已访问
for (int j = 0; j < adj[u].size(); j++) // 遍历 u 可以去的所有点
{
int v = adj[u][j].to, w = adj[u][j].w;
if (dis[v] > dis[u] + w) // 若从 u 去 v 更优,更改最短路,丢进队列
{
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}