【算法学习笔记】05 网络流
【算法学习笔记】05 网络流
入个大坑
1. 基本概念
1.1 流网络
不考虑反向边
记图为
有一个源点 和汇点
1.2 可行流
不考虑反向边
1.2.1 条件
容量限制:
流量守恒:中间点不储存流量
1.2.2 流量值
流出 - 流入
1.2.3 最大流
最大可行流
1.3 残留网络
考虑反向边
由可行流决定, 中所有反向边,即
原图的可行流残留网络的可行流 原图的另一个可行流
1.4 增广路径
残量网络中,沿着流量 的边走可以走到终点的路径
1.5 割
1.5.1 定义
把所有点不重不漏的分为两个集合 , 即
源点 , 汇点 , 且
1.5.2 割的容量
不考虑反向边
1.5.3 割的流量
考虑反向边
有
1.5.4
对于任意可行流 , 任意割 ,
1.5.5
对于任意可行流 , 任意割 ,
1.5.6 最大流最小割定理
- 流 是最大流
- 流 的残量网络中不存在增广路
- 存在某个割 ,
1.6 算法
1.6.0 FF思想
维护残留网络
每次:找增广路 + 更新残留网络(正向, 反向)
存图:邻接表,正反向边成对存储
1.6.1 算法
#include <bits/stdc++.h>
using namespace std;
const int N = 1005, M = 10005 * 2, inf = 1e9;
int n, m, S, T;
int h[N], e[M], ne[M], w[M], idx;
bool vis[N];
int d[N], pre[N];
void add (int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
bool bfs () {
queue<int> q;
memset (vis, false, sizeof vis);
memset (d, 0, sizeof d);
memset (pre, 0, sizeof pre);
q.push (S), vis[S] = true, d[S] = inf;
while (!q.empty()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (vis[j] || w[i] == 0) continue;
pre[j] = i;
vis[j] = true;
d[j] = min (d[t], w[i]);
if (j == T) return true;
q.push (j);
}
}
return false;
}
int EK () {
int ans = 0;
while (bfs ()) {
ans += d[T];
for (int i = T; i != S; i = e[pre[i]^1]) {
w[pre[i]] -= d[T], w[pre[i]^1] += d[T];
}
}
return ans;
}
int main () {
memset (h, -1, sizeof h);
cin >> n >> m >> S >> T;
while (m --) {
int a, b, c;
cin >> a >> b >> c;
add (a, b, c), add (b, a, 0);
}
cout << EK() << endl;
}
1.6.2 算法
不断找增广路。
优化之处在于,每次把可以增广的路全部增广一遍。
处理环:分层图
- 建立分层图
- 找出所有增广路径
当前弧优化:一条边增广一次后就不会再次增广了, 把h数组复制一份,不断更新增广的起点;
把已经流满的路径跳过:;
无法达到汇点则退出
#include <bits/stdc++.h>
using namespace std;
const int N = 10005, M = 200005, inf = 1e9;
int n, m, S, T;
int h[N], e[M], ne[M], w[M], idx;
int d[N], cur[N];
void add (int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
bool bfs () {
queue <int> q;
memset (d, -1, sizeof d);
q.push (S), d[S] = 0, cur[S] = h[S];
while (!q.empty ()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] != -1 || w[i] == 0) continue;
d[j] = d[t] + 1;
cur[j] = h[j];
if (j == T) return true;
q.push (j);
}
}
return false;
}
int find (int u, int limit) {
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i; //当前弧优化
int j = e[i];
if (d[j] != d[u] + 1 || w[i] == 0) continue;
int t = find (j, min (w[i], limit - flow));
if (t == 0) d[j] = -1;
w[i] -= t, w[i^1] += t, flow += t;
}
return flow;
}
int dinic () {
int r = 0, flow;
while (bfs ()) while (flow = find (S, inf)) r += flow;
return r;
}
int main () {
cin >> n >> m >> S >> T;
memset (h, -1, sizeof h);
while (m --) {
int a, b, c;
cin >> a >> b >> c;
add (a, b, c), add (b, a, 0);
}
cout << dinic () << endl;
}
//Dinic
1.7 应用
1.7.1 二分图
1. 二分图匹配
2. 二分图多重匹配
1.7.2 上下界网络流
1. 无源汇上下界可行流
2. 有源汇上下界最大流
3. 有源汇上下界最小流
1.7.3 多源汇最大流
题目
补充
分类:
算法原理