图论 Algorithms
1) Dijkstra
基本思路:更新每个点到原点的最短路径;寻找最短路径点进行下一次循环;循环次数达到 n - 1 次说明每个点到原点的最短路已成,停止程序。
1 function Dijkstra(Graph, source):
2
3 create vertex set Q
4
5 for each vertex v in Graph: // Initialization
6 dist[v] ← INFINITY // Unknown distance from source to v
7 prev[v] ← UNDEFINED // Previous node in optimal path from source
8 add v to Q // All nodes initially in Q (unvisited nodes)
9
10 dist[source] ← 0 // Distance from source to source
11
12 while Q is not empty:
13 u ← vertex in Q with min dist[u] // Source node will be selected first
14 remove u from Q
15
16 for each neighbor v of u: // where v is still in Q.
17 alt ← dist[u] + length(u, v)
18 if alt < dist[v]: // A shorter path to v has been found
19 dist[v] ← alt
20 prev[v] ← u
21
22 return dist[], prev[]
// Dijkstra
#include <bits/stdc++.h>
using namespace std;
const int N = 25000 + 5, M = 62000 + 5;
const int inf = 2147483647;
int n, m, s, e, dis[N];
int first[N], nex[M], to[M], w[M], en;
bool v[N];
void add(int x, int y, int z) {
nex[++en] = first[x]; first[x] = en; to[en] = y; w[en] = z;
nex[++en] = first[y]; first[y] = en; to[en] = x; w[en] = z;
}
int main() {
int x, y, z;
scanf("%d%d%d%d", &n, &m, &s, &e);
for (int i = 1; i <= n; i++)
dis[i] = inf;
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
}
dis[s] = 0; v[s] = true;
int k = s;
for (int i = 1; i <= n - 1; i++) {
int minlen = inf;
for (int j = first[k]; j; j = nex[j])
if (dis[k] + w[j] < dis[to[j]])
dis[to[j]] = dis[k] + w[j];
for (int j = 1; j <= n; j++) {
if ((!v[j]) && (dis[j] < minlen)) {
k = j;
minlen = dis[j];
}
}
v[k] = true;
}
printf("%d\n", dis[e]);
return 0;
}
/* Dijkstra Optimized
* Au: H15teve
*/
#include <bits/stdc++.h>
// header <queue> included.
using namespace std;
const int N = 200003, M = 1000003, inf = 2147483647;
struct node {
int u, dis;
bool operator < (const node &n) const {
return dis > n.dis;
}
} temp;
priority_queue<node> q;
int d[N], n, m, s, head[N], nex[M], to[M], w[M], en;
bool v[N];
inline void add(int x, int y, int z) {
nex[++en] = head[x], head[x] = en,
to[en] = y, w[en] = z;
}
int main() {
scanf("%d%d%d", &n, &m, &s);
for (int i = 1; i <= n; i++) d[i] = inf;
for (int i = 1, x, y, z; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
}
d[s] = 0, temp.u = s; q.push(temp);
while (!q.empty()) {
temp = q.top(), q.pop();
if (v[temp.u]) continue;
v[temp.u] = true;
for (int i = head[temp.u]; i; i = nex[i])
if (temp.dis + w[i] < d[to[i]])
d[to[i]] = temp.dis + w[i],
q.push((node) {to[i], d[to[i]]});
}
for (int i = 1; i <= n; i++)
printf("%d ", d[i]);
return 0;
}
2) Kruskal
基本思路:按边长度从小到大排序,循环添加「不成环」的边;边数达到 n - 1 说明最小生成树已成,停止程序。
/* Kruskal
* Au: GG
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1000 + 3, M = 20000 + 3;
int dset[N], n, m;
struct edge {
int x, y, w;
bool operator < (const edge &a) const { return w < a.w; }
} edges[M];
int find(int x) { return (dset[x] == -1) ? x : dset[x] = find(dset[x]); }
void join(int x, int y) { if (find(x) != find(y)) dset[find(x)] == find(y); }
int Kruskal() {
memset(dset, -1, sizeof(dset));
sort(edges + 1, edges + m + 1);
int cnt = 0, tot = 0;
for (int i = 1; i <= m; i++) //循环所有已从小到大排序的边
if (find(edges[i].x) != find(edges[i].y)) { // (因为已经排序,所以必为最小)
join(edges[i].x, edges[i].y); // 相当于把边(u,v)加入最小生成树。
tot += edges[i].w;
cnt++;
if (cnt == n - 1) // 说明最小生成树已经生成
break;
}
return tot;
}
int main() {
printf("Enter vertex number & edge number:\n");
scanf("%d%d", &n, &m);
printf("Enter information of %d edge(s):\n", m);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &edges[i].x, &edges[i].y, &edges[i].w);
}
printf("The weight of minimum spanning tree is %d.\n", Kruskal());
return 0;
}
3) Bellman-Ford
基本思路:更新每个点到原点的最短路径;循环次数达到 n - 1 次说明每个点到原点的最短路已成,停止程序。
// Bellman-Ford
// Au: GG
#include <bits/stdc++.h>
using namespace std;
const int maxn = 25000 + 5, maxm = 62000 + 5;
const int inf = 1000000001;
int n, m, s, e, en;
int from[maxm], to[maxm], w[maxm];
int dis[maxn], pre[maxm];
void add(int x, int y, int z) {
from[++en] = x; to[en] = y; w[en] = z;
}
int main() {
int x, y, z;
scanf("%d%d%d%d", &n, &m, &s, &e);
for (int i = 1; i <= n; i++)
dis[i] = inf;
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
}
dis[s] = 0; pre[s] = 0;
for (int i = 1; i <= n - 1; i++)
for (int j = 1; j <= m; j++)
if (dis[from[j]] + w[j] < dis[to[j]]) {
dis[to[j]] = dis[from[j]] + w[j];
pre[to[j]] = from[j];
}
printf("%d\n", dis[e]);
return 0;
}
4) Floyd
基本思路:枚举所有点与点的中点,如果从中点走最短,更新两点间距离值。
procedure Floyd–Warshall(G)
1 let dist be a |V| × |V| array of minimum distances initialized to ∞ (infinity)
2 for each vertex v
3 dist[v][v] ← 0
4 for each edge (u,v)
5 dist[u][v] ← w(u,v) // the weight of the edge (u,v)
6 for k from 1 to |V|
7 for i from 1 to |V|
8 for j from 1 to |V|
9 if dist[i][j] > dist[i][k] + dist[k][j]
10 dist[i][j] ← dist[i][k] + dist[k][j]
11 end if
// Floyd
// Au: GG
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 3;
const int inf = 1000000001;
int n, m;
int f[maxn][maxn];
int main() {
scanf("%d%d", &n, &m);
int i, j, k, a, b, w;
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
f[i][j] = inf;
for (i = 1; i <= m; i++) {
scanf("%d%d%d", &a, &b, &w);
if (f[a][b] > w) f[a][b] = w, f[b][a] = w;
}
for (k = 1; k <= n; k++)
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (f[i][j] > f[i][k] + f[k][j])
f[i][j] = f[i][k] + f[k][j];
scanf("%d%d", &a, &b);
printf("%d", f[a][b]);
return 0;
}
5) SPFA
基本思路:更新每个点到原点的最短路径,保证「路径可变得更小的点」在队列中;队列空说明每个点到原点的最短路已成,停止程序。
procedure Shortest-Path-Faster-Algorithm(G, s)
1 for each vertex v ≠ s in V(G)
2 d(v) := ∞
3 d(s) := 0
4 offer s into Q
5 while Q is not empty
6 u := poll Q
7 for each edge (u, v) in E(G)
8 if d(u) + w(u, v) < d(v) then
9 d(v) := d(u) + w(u, v)
10 if v is not in Q then
11 offer v into Q
/* Shortest Path Faster Algorithm
* Au: GG
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1000 + 3, M = 10000 + 3;
int n, m, s, d[N], pre[N], enq[N];
bool inq[N];
int head[N], nex[M], to[M], w[N], en;
queue<int> q;
void add(int x, int y, int z) {
nex[++en] = head[x]; head[x] = en; to[en] = y; w[en] = z;
nex[++en] = head[y]; head[y] = en; to[en] = x; w[en] = z;
}
bool SPFA() {
memset(d, 0x7f, sizeof(d));
q.push(s); d[s] = 0; inq[s] = true; enq[s]++;
while (!q.empty()) {
int a = q.front(); q.pop();
inq[a] = false;
for (int b = head[a]; b; b = nex[b])
if (d[a] + w[b] < d[to[b]]) {
d[to[b]] = d[a] + w[b];
pre[to[b]] = a;
if (!inq[to[b]]) {
q.push(to[b]);
enq[to[b]]++; if (enq[to[b]] >= n) return false;
inq[to[b]] = true;
}
}
}
return true;
}
int main() {
int a, b, c;
printf("Enter vertex number, edge number & source point index:\n");
scanf("%d%d%d", &n, &m, &s);
printf("Enter information of %d edge(s):\n", m);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
if (SPFA()) {
for (int i = 1; i <= n; i++)
if (i != s) {
printf("Distance between source point to point %d is %d.\n", i, d[i]);
int p = i;
stack<int> ans;
printf(" Path: %d", s);
while (s != p) ans.push(p), p = pre[p];
while (!ans.empty()) printf(" -> %d", ans.top()), ans.pop();
printf("\n");
}
} else printf("The graph has negative circle!\n");
return 0;
}
「重拾最短路」可参考 61mon.com 的博客:
Reference
- internal (blog/83) by SJoshua
- Stack Overflow. Trying to understand Dijkstra's Algorithm[DB/OL]. Link , 2017-08-04
Post author 作者: Grey
Copyright Notice 版权说明: Except where otherwise noted, all content of this blog is licensed under a CC BY-NC-SA 4.0 International license. 除非另有说明,本博客上的所有文章均受 知识共享署名 - 非商业性使用 - 相同方式共享 4.0 国际许可协议 保护。