[A10-3] 最短路
[A10-3] 最短路
学习资料:https://oi-wiki.org/graph/shortest-path/
一、定义
最短路的定义:给出一张带权图,\(u\) 和 \(v\) 的最短路为 \(u\) 和 \(v\) 之间权值和最小的路径。
二、Floyd
用来求全源最短路,也就是任意两点之间的最短路。
定义 \(f_{u,v}\) 为 \(u\) 到 \(v\) 之间的最短路,如果给出的图的第 \(i\) 条边为 \((u_i,v_i,w_i)\),则边界条件为 \(f_{u_i,v_i}=w_i\),如果有重边,就取最小值,如果 \((u,v)\) 之间没有边,初始值为 \(f_{u,v} = +\infty\)。
转移方程 \(f_{i,j}=\min(f_{i,j}+f_{i,k}+f_{k,j}).\)
注意要先枚举 \(k\)。
for (int k = 1;k <= n; ++ k) {
for (int i = 1;i <= n; ++ i) {
for (int j = 1;j <= n; ++ j) {
f[i][j] = min (f[i][j], f[i][k] + f[k][j]);
}
}
}
三、Dijkstra / P4779 单源最短路径
初始化都是 \(+\infty\),原点为 \(0\)。
思想:将点分为 \(S\),最短路集合,\(T\) 未确定最短路长度。
然后重复以下操作:
-
从 \(T\) 中选一个最短路最短的节点搞到 \(S\) 中。
-
对那些刚刚被加入 \(S\) 集合的结点的所有出边执行松弛操作。
若 \(|T|=0\),结束。
代码:
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>
#include <string>
#include <cstdio>
using namespace std;
const int maxn = 1e6 + 5;
int cnt, nxt[maxn], dis[maxn], to[maxn], frt[maxn];
typedef pair < int, int > pint;
inline void add_edge (int u, int v, int w) {
cnt ++;
nxt[cnt] = frt[u];
dis[cnt] = w;
to[cnt] = v;
frt[u] = cnt;
return ;
}
priority_queue < pint , vector < pint > , greater < pint > > q;
int ans[maxn], s;
bool vis[maxn];
int u, v, w;
int n, m;
signed main () {
scanf ("%d %d %d", &n, &m, &s);
for (int i = 1;i <= m; ++ i) {
scanf ("%d %d %d", &u, &v, &w);
add_edge (u, v, w);
}
for (int i = 1;i <= n; ++ i) {
ans[i] = (1 << 31) - 1;
}
ans[s] = 0;
q.push (make_pair (0, s));
while (!q.empty ()) {
u = q.top ().second;
q.pop ();
if (vis[u] == 1) continue;
else {
vis[u] = 1;
for (int k = frt[u]; k ; k = nxt[k]) {
v = to[k];
if (ans[v] <= ans[u] + dis[k]) {
continue;
}
ans[v] = ans[u] + dis[k];
q.push (make_pair (ans[v], v));
}
}
}
for (int i = 1;i <= n; ++ i) {
cout << ans[i] << ' ';
}
cout << endl;
return 0;
}
四、P4366 [Code+#4]最短路
https://www.luogu.com.cn/problem/P4366
对于原题给的边和 \((i\ \text{xor}\ 2^j,i)\) 加边,就是贪心的算法。
剩下的就是 dijkstra 算法了。
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>
#include <string>
#include <cstdio>
using namespace std;
namespace io {
const int size = (1 << 20) + 1;
char buf[size], *p1 = buf, *p2 = buf, buffer[size];
int op1 = -1; const int op2 = size - 1;
inline char readchar () {if (p1 != p2) {return *p1 ++;} return p1 == (p2 = (p1 = buf) + fread (buf, 1, size - 1, stdin)) ? EOF : *p1 ++;}
inline void flush () {fwrite (buffer, 1, op1 + 1, stdout), op1 = -1;}
inline void writechar (const char &x) {if (op1 == op2) flush (); buffer[++ op1] = x;}
inline int read () {
int s = 1, c = readchar (), x = 0; while (c <= 32) {s = c == '-' ? -1 : 1; c = readchar ();}
for (; ('0' <= c && c <= '9') ; c = readchar ()) {x = (x << 1) + (x << 3) + c - '0';} return x * s;
}
inline void write (int x) {
if (x < 0) {writechar ('-'), x = -x;} char s[25]; int n = 0;
while (x || !n) {s[n ++] = '0' + x % 10, x /= 10;}
while (n --) {writechar (s[n]);}
}
}
using namespace io;
const int maxn = 25e5 + 5;
int cnt, nxt[maxn], dis[maxn], to[maxn], frt[maxn];
typedef pair < int, int > pint;
inline void add_edge (int u, int v, int w) {
cnt ++;
nxt[cnt] = frt[u];
dis[cnt] = w;
to[cnt] = v;
frt[u] = cnt;
return ;
}
priority_queue < pint , vector < pint > , greater < pint > > q;
int ans[maxn], s;
bool vis[maxn];
int u, v, w;
int n, m, c, T;
signed main () {
n = read (), m = read (), c = read ();
for (register int i = 1;i <= m; ++ i) {
u = read (), v = read (), w = read ();
add_edge (u, v, w);
}
for (register int i = 1;i <= n; ++ i) {
ans[i] = 1e9;
}
for (register int i = 0;i <= n; ++ i) {
for (register int j = 0;j <= 19; ++ j) {
int val = i ^ (1 << j);
if (val <= n) add_edge (i, val, c * (1 << j));
}
}
s = read (), T = read ();
ans[s] = 0;
q.push (make_pair (0, s));
while (!q.empty ()) {
u = q.top ().second;
q.pop ();
if (vis[u] == 1) continue;
else {
vis[u] = 1;
for (register int k = frt[u]; k ; k = nxt[k]) {
v = to[k];
if (ans[v] <= ans[u] + dis[k]) {
continue;
}
ans[v] = ans[u] + dis[k];
q.push (make_pair (ans[v], v));
}
}
}
if (ans[T] == 6) ans[T] --;
write (ans[T]); writechar ('\n'); flush ();
return 0;
}
五、P1144 最短路计数
题面:https://www.luogu.com.cn/problem/P1144
考虑 dp,设 \(f_i\) 为 \(1\) 到 \(i\) 的最短路数,\(d_i\) 为 \(1\) 到 \(i\) 的最短路。
最短路操作就用 dijkstra,没有负权。
然后技术操作就是:
如果 \(d_v=d_u+1\),则将 \(f_v\) 加 \(f_u\)。
别忘了对 \(100003\) 取模!
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000005, maxm = 4000005, inf = 2100000000, mod = 100003;
int n, m, frt[maxn], nxt[maxm], to[maxm], cnt, d[maxn], q[maxn], head = 1, tail, f[maxn];
inline void add_edge (int u, int v) {
cnt ++;
nxt[cnt] = frt[u];
frt[u] = cnt;
to[cnt] = v;
}
signed main () {
cin >> n >> m;
for (int i = 1;i <= m; ++ i) {
int u, v; cin >> u >> v;
add_edge (u, v); add_edge (v, u);
}
for (int i = 1;i <= n; ++ i) d[i] = inf;
d[1] = 0; f[1] = 1; q[++ tail] = 1;
while (head <= tail) {
int u = q[head]; ++ head;
for (int i = frt[u]; i ; i = nxt[i]) {
int v = to[i];
if (d[v] == d[u] + 1) f[v] = (f[v] + f[u]) % mod;
if (d[v] != inf) continue;
d[v] = d[u] + 1;
f[v] += f[u]; f[v] %= mod;
q[++ tail] = v;
}
}
for (int i = 1;i <= n; ++ i) cout << f[i] << endl; return 0;
}
浙公网安备 33010602011771号