\(u\) 到 \(v\) 的最短路径的长度就是 \(u\) 到 \(v\) 的最短路。
单源最短路算法可以求出一个点到其他点的最短路。
全源最短路算法可以求出每一个点到其他各点的最短路。
松弛操作:dis[v] = min(dis[v], dis[u] + w);
。
算法
Floyd 算法
全源最短路算法,时间复杂度 \(O_{n^3}\),空间复杂度 \(O_{n^2}\),用了 DP 的思想。
实现
设数组 f[x][y]
,含义:\(x\) 到 \(y\) 的最短路。
转移:f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
\(k\) 为除了 \(x\) 和 \(y\) 的其他节点。
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
for (int k = 1; k <= n; ++ k) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
dijkstra 算法
单源最短路算法,时间复杂度 \(O_{n^2}\),运用了贪心的思想。
实现
将点分为两部分,已经确定最短路的点和没确定最短路的点,记已确定最短路的点的集合为 \(\mathbf{S}\),未确定最短路的点的集合为 \(\mathbf{T}\)。
设 \(s\) 为起点,初始时设 dis[s] = 0
,其他节点的 dis
设为 \(\inf\)。
从 \(\mathbf{T}\) 中取一个最短路最小的点,将它转移到 \(\mathbf{S}\) 集合去,并用它来更新其他点的最短路,重复此操作,直到 \(\mathbf{T}\) 为空。
这里用最短路最小点的来更新答案就是运用了贪心的思想。
int n;
int dis[N], vis[N];
vector<pair<int, int> > son[N];
void dij(int s) {
for (int i = 0; i <= n; ++ i) {
dis[i] = inf;
}
dis[s] = 0;
for (int i = 1; i < n; ++ i) {
int u = 0, minn = inf;
for (int j = 1; j <= n; ++ j) {
if (vis[j] || dis[j] >= minn) continue;
u = j;
minn = dis[j];
}
vis[u] = true;
for (auto it : son[u]) {
int v = it.first, w = it.second;
dis[v] = min(dis[v], dis[u] + w);
}
}
}
优化
我们每次从剩下的元素中取最小的,进行 \(n\) 次,复杂度为 \(O_{n \log n}\),但是,我们可以用小根堆堆来使得这步操作的时间复杂度降低至 \(O_{n \log n}\),于是,就有了 dijkstra 的堆优化版本。
typedef pair<int, ll> pil;
void dij(int st) {
priority_queue<pil, vector<pil>, greater<pil> > q;
for (int i = 1; i <= n; ++i) {
dis[i] = 1e9;
vis[i] = 0;
}
dis[st] = 0;
q.push(make_pair(0, st));
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
q.push(make_pair(dis[v], v));
}
}
}
}
不管是朴素版本的 dijkstra,还是堆优化版本的 dijkstra,都要求图的边权不为负边权。
Bellman-Ford 算法
这个算法或许有些人没听说过,但是它的优化版本你肯定听过,SPFA。
单源最短路算法,时间复杂度 \(O_{nm}\)
实现
每一次松弛操作都会产生一条最短路,如果一个图中不存在负环,最多会松驰 \(n - 1\) 次。
for (int i = 1; i <= n; ++ i) {
dis[i] = inf;
}
dis[s] = 0;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
dis[v[j]] = min(dis[v[j]], dis[u[j]] + w[j]);
}
}
SPFA 算法
关于SPFA,它。。。
SPFA算法是 Bellman-Ford 算法的队列优化版本,最坏时间复杂度为 \(O_{nm}\),一般跑得很快。
实现
给原先的 Bellman-Ford 加一个队列即可。
void spfa(int s) {
queue<int> q;
for (int i = 1; i <= n; ++ i) {
dis[i] = inf;
vis[i] = 0;
}
dis[s] = 0;
q.push(s);
vis[s] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = 0;
for (auto it : son[u]) {
int v = it.first, w = it.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (! vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
}
标签:mathbf,int,短路,重修,笔记,算法,复杂度,dis
From: https://www.cnblogs.com/yifan0305/p/17363015.html