Dijkstra模板
Dijkstra 朴素版 O(n^2)
解决什么样的问题
Dijkstra 算法用于求解单源最短路径问题,适用于边权非负的有向图或无向图。算法通过贪心策略逐步确定从起点到各顶点的最短路径。
以 849Dijkstra 来描述朴素版的Dijkstra 算法 O(n^2)复杂度
https://www.acwing.com/problem/content/851/

算法思路
- 初始化:记录起点到各节点的距离,起点距离为 0,其余初始化为无穷大。
- 贪心选择:每次从未处理的节点中选出距离起点最近的节点。
- 松弛操作:用该节点更新其邻接节点的距离(若更短)。
- 标记完成:已处理的节点不再参与后续计算。
- 重复:直到所有节点均被处理。
算法流程
- 输入处理与初始化
- 使用邻接矩阵g[][]存储边权,初始化为 INF(表示无穷大)。
- 处理重边时,保留最短边:g[x][y] = min(g[x][y], z)。
- 初始化距离数组 dist[],dist[1] = 0,其余为 INF。
- 标记数组 st[] 记录节点是否已处理。
- 主循环(进行 n 次)
- 选择当前最短节点:遍历所有节点,找到未处理且距离最小的节点 t。
- 标记已处理:st[t] = true。
- 松弛邻接节点:遍历所有节点 j,尝试通过 t 缩短路径:
dist[j] = min(dist[j], dist[t] + g[t][j])。
- 输出结果
- 若终点 dist[n] 仍为 INF,说明不可达,输出 -1。
- 否则输出 dist[n]。
关键细节
- 邻接矩阵的初始化
所有边初始化为 INF,读入时处理重边,确保保留最短边。 - 贪心选择的实现
每次遍历所有节点,选择未处理的最小距离节点。时间复杂度为 O(n²),适合节点数较少的情况(如本题 n ≤ 500)。 - 松弛操作的逻辑
即使 g[t][j] 为 INF(无边),dist[j] 不会被错误更新,因为 dist[t] + INF 仍为极大值,不影响 min 操作。 - 无穷大的设置
使用 0x3f3f3f3f(约 1e9)表示 INF,其两倍不会溢出,且便于判断未达节点。 - 处理自环与重边
自环不影响结果(正权边无法缩短路径),重边通过取最小值处理。
常见问题
- 为什么不能处理负权边?
负权可能导致已处理的节点通过更短路径重新被访问,破坏贪心策略的正确性。 - 如何处理不可达的情况?
若终点距离仍为初始化的 INF,则不可达。 - 为何选择邻接矩阵?
节点数较少(n ≤ 500),邻接矩阵实现简单且效率可接受。边数较多时,需改用堆优化(O(m log n))。
code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5e2 + 10; // 最大节点数+10的缓冲区
int n, m; // n: 节点数, m: 边数
int g[N][N]; // 邻接矩阵存储图结构(处理稠密图)
int dist[N]; // 存储起点到各点的最短距离
bool st[N]; // 标记节点是否已确定最短路径
int Dijkstra()
{
memset(dist, 0x3f, sizeof dist); // 初始化所有距离为无穷大
dist[1] = 0; // 起点到自身的距离为0
// 进行n次迭代,每次确定一个节点的最短路径
for (int i = 1; i <= n; i++)
{
int t = -1;
// 找出当前未确定最短路径的节点中距离最小的
for (int j = 1; j <= n; j++)
{
if (!st[j] && (t == -1 || dist[j] < dist[t]))
t = j;
}
st[t] = true; // 标记该节点已确定最短路径
// 通过当前节点t更新其他节点的距离
for (int j = 1; j <= n; j++)
{
// 松弛操作:尝试用 dist[t] + g[t][j] 更新 dist[j]
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
}
// 判断终点是否可达
if (dist[n] == 0x3f3f3f3f)
return -1;
return dist[n];
}
int main()
{
cin >> n >> m;
memset(g, 0x3f, sizeof g); // 初始化邻接矩阵为无穷大
// 处理输入边,注意处理重边保留最小值
for (int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
g[x][y] = min(g[x][y], z); // 对于重边,只保留最短的边
}
int ans = Dijkstra();
cout << ans << endl;
return 0;
}
Dijkstra 堆优化版 O(nlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 150005; // 最大节点数+10的缓冲区
int n, m; // n: 节点数, m: 边数
int dist[N]; // 存储起点到各点的最短距离
bool st[N]; // 标记节点是否已确定最短路径
typedef pair<int, int> PII;
int ne[N], h[N], e[N], w[N], idx;
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int Dijkstra()
{
memset(dist, 0x3f, sizeof dist); // 初始化所有距离为无穷大
dist[1] = 0; // 起点到自身的距离为0
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1});
while (heap.size())
{
auto t = heap.top();
heap.pop();
int distance = t.first, ver = t.second;
if (st[ver])
continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j], j});
}
}
}
// 判断终点是否可达
if (dist[n] == 0x3f3f3f3f)
return -1;
return dist[n];
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
int ans = Dijkstra();
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号