网络流学习笔记
关于什么是网络流
相当于我们有一张有向图 \(G=(V,E)\)。对于每条边,都有它的容量和流量,即 \(c(u,v),f(u,v)\)。同时给定 \(s\) 作为源点,\(t\) 作为汇点。对于一个函数 \(f(u,v)\) 我们称 \(u\) 流出了 \(f(u,v)\),\(v\) 汇入了 \(f(u,v)\)。
-
对于每个点(除 source 和 sink),不储存任何流,也就是每个点流出的总量等于汇入的总量。
-
\((u,v) \notin E,c(u,v)=0\)。
-
\(f(u,v)\le c(u,v)\)。
注意 \(f(u,v)\) 的值域为 \(Z\)。
同时 \(s\) 点的流出和为整张图的流量。
最大流
我们需要找到整张图最大的流量。
我们先来介绍 FF 算法,其实,这是接下来所有算法的模型,也就是加下来这些算法的基础。
增广路:一条从 \(s\) 到 \(t\) 的路径,对于路径上的每条边,\((u,v)\in P,f(u,v) < c(u,v)\),即每条边都在残量网络 \(G_{f}\) 里。
对于 FF 算法,想的就是,我们抽一条增广路增广。然后整个网络的流量和增广路上的流量增加。但是这样并不能保证一定最优,所以我们仿照反悔贪心,同时给所有增广路上的反边流量减少同样的值。这样我们在增广的时候也可以选取反边就相当于反悔了。这样做直到没有一条增广路,就可以得到答案。
这类算法时间复杂度上界是 \(O(mf)\) 的,\(f\) 是 \(G\) 上的最大流,显然每次流量都会增加,每次最多遍历 \(m\) 条边。
接下来我们来学习 EK 算法,它的增广措施如下:
从 \(s\) BFS 到 \(t\),其中对于路径上的每条边增加 \((u,v) \in G_{f},\min\{c(u,v)-f(u,v)\}\)。同时所有反边减少 \((u,v) \in G_{f},\min\{c(u,v)-f(u,v)\}\),这样直到没有一条增广路,就是答案。
时间复杂度:\(O(nm^2)\)。
想要更高明的增广?详见 Dinic 算法。
Dinic 算法会算出每个点的距离函数 \(D(u)\) 表示点到源点的最短距离。它仅会保留原图中 \(D(v)=D(u)+1\) 的边。
然后在建出的新图中,寻找最大的增广流(阻塞流),使得新图中上不可能找出更大的增广流。
这样的时间复杂度是 \(O(n^2m)\) 的。
下面给出实现:
// Problem: P3376 【模板】网络最大流
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3376
// Memory Limit: 512 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define int long long
#define upp(a, x, y) for (int a = x; a <= y; a++)
#define dww(a, x, y) for (int a = x; a >= y; a--)
#define pb(x) push_back(x)
#define endl '\n'
#define x first
#define y second
#define PII pair<int, int>
using namespace std;
const int N = 210, M = 1e4 + 10, INF = 0x3f3f3f3f3f3f3f3f;
int h[N], hh[N], ne[M], e[M], flow[M], lim[M], dep[N], idx, cnt, maxflow;
int n, m, S, T;
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void init() {
memset(h, -1, sizeof h);
cnt = 0;
}
bool bfs() {
queue<int> q;
memset(dep, 0, sizeof dep);
dep[S] = 1;
q.push(S);
while (q.size()) {
auto iter = q.front();
q.pop();
for (int i = h[iter]; i != -1; i = ne[i]) {
int j = e[i];
if ((!dep[j]) && (flow[i] < lim[i])) {
dep[j] = dep[iter] + 1;
q.push(j);
}
}
}
return dep[T];
}
int dfs(int u, int fl) {
if (u == T || !fl) return fl;
int ret = 0;
for (int &i = hh[u]; i != -1; i = ne[i]) {
int j = e[i], d;
if (dep[j] != dep[u] + 1) continue;
if (!(d = dfs(j, min(fl - ret, lim[i] - flow[i])))) continue;
ret += d;
flow[i] += d;
flow[(i ^ 1)] -= d;
if(ret==fl) return ret;
}
return ret;
}
void dinic() {
while (bfs()) {
memcpy(hh, h, sizeof hh);
maxflow += dfs(S, INF);
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
init();
cin >> n >> m >> S >> T;
upp(i, 1, m) {
int x, y, z;
cin >> x >> y >> z;
lim[idx] = z;
add(x, y);
lim[idx] = 0;
add(y, x);
}
dinic();
cout << maxflow << endl;
return 0;
}