定义:
1. 流网络:不考虑反向边,若存在反向边可以加一个点使其变得不存在反向边
2. 可行流:需要满足容量限制(0 <= f(u,v) <= c(u,v) )和流量守恒
3. 流量:从源点流出的流量减去流进原点的流量
4. 残留网络:对于流网络中的某一条可行流来说的(点等于原网络的所有点,边为原网络的边和原网络的反向边)【有可行流的概念】(只有在残留网络中才会考虑反向边)
原网络的流量(f) + 残留网络的流量(f') 也是 原网络的流量
5. 增广路径:从原点开始沿着容量大于0的边开始走,走到终点的路径(流量大于0的可行流)
6. 割:将原网络的点集分成两个子集S和T(相交为空,且s属于S,t属于T)
7. 割的容量:从S指向T的容量之和(割的分法一共有2的n-2次方种)
8. 割的流量:从S指向T的流量 - 从T指向S的流量
9. 最大流:最大可行流的流量 最小割:指的是最小割的容量
10. 对于任意一个可行流任意割,割的流量都等于该可行流的流量
最大流最小割定理:
1. 可行流f是最大流
2. 可行流f的残留网络中不存在增广路
3. 存在某个割[S,T], |f| = c(S, T)
证明建图是正确的:1.原网络的可行流都是原问题的一个可行解 2. 原问题的一个可行解都是原网络的可行流
模板
EK(时间复杂的 n*m*m)【n=1000 ~ 10000】
#include<bits/stdc++.h>
using namespace std;
const int N = 1010, M = 20010;
const int INF = 1e9;
int n, m, S, T;
// 存的是残留网络的图
int e[M], ne[M], h[M], f[M], idx;
void add(int u, int v, int c){
e[idx] = v, ne[idx] = h[u], f[idx] = c, h[u] = idx++; // f为容量
e[idx] = u, ne[idx] = h[v], f[idx] = 0, h[v] = idx++;
}
bool st[N];
int q[N], d[N], pre[N];
bool bfs(){
int hh = 0, tt = 0;
memset(st, false, sizeof st);
q[0] = S, st[S] = true, d[S] = INF;
while(hh <= tt){
int u = q[hh++];
for(int i = h[u]; ~i; i = ne[i]){
int v = e[i];
if(!st[v] && f[i]){
st[v] = true;
d[v] = min(d[u], f[i]);
pre[v] = i;
if(v == T) return true;
q[++ tt] = v;
}
}
}
return false;
}
int EK(){
int res = 0;
while(bfs()){ // 找增广路劲
res += d[T];
for(int i = T; i != S; i = e[pre[i] ^ 1])
f[pre[i]] -= d[T], f[pre[i] ^ 1] += d[T]; // 更新残留网络
}
return res;
}
int main(){
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &m, &S, &T);
for(int i = 1; i <= m; i++){
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
add(u, v, c);
}
printf("%d\n", EK());
return 0;
}
dinic(时间复杂度n*n*m)【n=10000 ~ 100000】
#include<bits/stdc++.h>
using namespace std;
const int N = 10010, M = 200010;
const int INF = 1e9;
int n, m, S, T;
int e[M], ne[M], h[M], f[M], idx;
void add(int u, int v, int c){
e[idx] = v, ne[idx] = h[u], f[idx] = c, h[u] = idx++; // f为容量
e[idx] = u, ne[idx] = h[v], f[idx] = 0, h[v] = idx++;
}
int d[N], q[N], cur[N];
bool bfs(){
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while(hh <= tt){
int u = q[hh++];
for(int i = h[u]; ~i; i = ne[i]){
int v = e[i];
if(d[v] == -1 && f[i]){
d[v] = d[u] + 1;
cur[v] = h[v];
if(v == T) return true;
q[++tt] = v;
}
}
}
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 v = e[i];
if(d[v] == d[u] + 1 && f[i]){
int t = find(v, min(f[i], limit - flow));
if(!t) d[v] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic(){
int res = 0, flow;
while(bfs()) while(flow = find(S, INF)) res += flow;
return res;
}
int main(){
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &m, &S, &T);
for(int i = 1; i <= m; i++){
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
add(u, v, c);
}
printf("%d\n", dinic());
return 0;
}