[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\) 未确定最短路长度。

然后重复以下操作:

  1. \(T\) 中选一个最短路最短的节点搞到 \(S\) 中。

  2. 对那些刚刚被加入 \(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;
}

posted on 2022-05-07 19:34  一只超级大蒟蒻  阅读(61)  评论(0)    收藏  举报

导航