dijkstra 算法笔记
dijkstra 的本质是贪心。
首先设 \(dis_i\) 为从 \(s\to i\) 的最短路(\(s\) 为起点),贪心过程其实就是,找到所有确定了最短路的点中最短路最短的点记为 \(i\),它的最短路一定是确定了的,然后将其松弛:
\[dis_j=\min\{dis_j,dis_i+w\}
\]
\(j\) 为 \(i\) 的相邻点,\(w\) 为 \(i\to j\) 这条边的权值。
\(dis_i\) 初始值为正无穷。
于是就得到了代码:
#include <bits/stdc++.h>
using namespace std;
using Pii = pair<int, int>;
const int kMaxN = 2e5 + 5, kInf = 1e9 + 7;
int T, n, m, s, t, dis[kMaxN];
bool vis[kMaxN];
vector<Pii> e[kMaxN];
void dijkstra(int s, int n) {
fill(vis + 1, vis + 1 + n, 0);
fill(dis + 1, dis + 1 + n, kInf); // 初始化。
dis[s] = 0;
for (int t = 1; t <= n; t++) {
int u = -1;
for (int i = 1; i <= n; i++) {
if (!vis[i] && (u == -1 || dis[i] < dis[u])) {
u = i;
} // 找到所有确定了最短路的点中最短路最短的点。
}
vis[u] = 1; // 最短路确定。
for (auto i : e[u]) { // 松弛相邻的点。
int v = i.first, w = i.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
}
}
}
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
for (cin >> T; T-- && cin >> n >> m >> s >> t;) {
for (int i = 1; i <= n; i++) {
e[i].clear();
}
for (int i = 1, u, v, w; i <= m; i++) {
cin >> u >> v >> w;
e[u].emplace_back(v, w);
e[v].emplace_back(u, w);
}
dijkstra(s, n);
if (dis[t] == kInf) {
cout << "NONE\n";
} else {
cout << dis[t] << "\n";
}
} // 多组询问。
return 0;
}
我们可以发现,这一段代码是没有必要的:
for (int i = 1; i <= n; i++) {
if (!vis[i] && (u == -1 || dis[i] < dis[u])) {
u = i;
}
}
其实可以用堆优化,也就是 priority_queue
来做这个代码。
时间复杂度从 \(O(n^2)\) 变成了\(O((n+m)\log_{2}{n})\) 的时间复杂度。
#include <bits/stdc++.h>
using namespace std;
using Pii = pair<int, int>;
const int kMaxN = 2e5 + 5, kInf = 1e9 + 7;
int T, n, m, s, t, dis[kMaxN];
bool vis[kMaxN];
vector<Pii> e[kMaxN];
void dijkstra(int s, int n) {
fill(vis + 1, vis + 1 + n, 0);
fill(dis + 1, dis + 1 + n, kInf); // 初始化数组。
priority_queue<Pii, vector<Pii>, greater<Pii> > q; // 堆优化。
for (dis[s] = 0, q.emplace(0, s); !q.empty();) {
Pii now = q.top();
q.pop();
int u = now.second;
if (vis[u]) {
continue; // 找到所有确定了最短路的点中最短路最短的点。
}
vis[u] = 1;
for (auto i : e[u]) {
int v = i.first, w = i.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.emplace(dis[v], v);
}
}
}
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
for (cin >> T; T-- && cin >> n >> m >> s >> t;) {
for (int i = 1; i <= n; i++) {
e[i].clear();
}
for (int i = 1, u, v, w; i <= m; i++) {
cin >> u >> v >> w;
e[u].emplace_back(v, w);
e[v].emplace_back(u, w);
}
dijkstra(s, n);
if (dis[t] == kInf) {
cout << "NONE\n";
} else {
cout << dis[t] << "\n";
}
} // 多组询问。
return 0;
}
dijkstra 的本质是通过寻找最短路最小的点,以此可以确定这个点的最短路,但是如果图中有负权边的话,最短路就不一定了,这种情况可以使用势能 dijkstra 或者 spfa。
例题: