流网络
上图即为一个流网络
G
=
V
,
E
G = {V, E}
G=V,E。
流网络是一个有向图,有两个特殊点,成为原点
s
s
s 和汇点
t
t
t,每条边
(
u
,
v
)
(u, v)
(u,v) 有一个属性——容量
c
(
u
,
v
)
c(u, v)
c(u,v)。
可行流
可行流 f f f 是指定流网络的每一条边的流量 f ( u , v ) f(u, v) f(u,v),只要满足两个条件就是一个可行流。
- 容量限制 0 ≤ f ( u , v ) ≤ c ( u , v ) \displaystyle 0 \le f(u, v) \le c(u, v) 0≤f(u,v)≤c(u,v)
- 流量守恒 ∀ x ∈ V − { s , t } ∑ ( v , x ) ∈ E f ( v , x ) = ∑ ( x , v ) ∈ E f ( x , v ) \displaystyle \forall x \in V - \{s, t\} \sum_{(v, x) \in E} f(v, x) = \sum_{(x, v) \in E} f(x, v) ∀x∈V−{s,t}(v,x)∈E∑f(v,x)=(x,v)∈E∑f(x,v)
在这里我们不考虑反向边,因为这样很好理解,如果有反向边就建正反方向两条边就可以了。
一个可行流
f
f
f 有一个流量
∣
f
∣
|f|
∣f∣。
∣
f
∣
=
∑
(
s
,
x
)
∈
E
f
(
s
,
x
)
−
∑
(
x
,
s
)
∈
E
f
(
x
,
s
)
\displaystyle |f| = \sum_{(s, x) \in E} f(s, x) - \sum_{(x, s) \in E} f(x, s)
∣f∣=(s,x)∈E∑f(s,x)−(x,s)∈E∑f(x,s)
=
∑
(
x
,
t
)
∈
E
f
(
x
,
t
)
−
∑
(
t
,
x
)
∈
E
f
(
T
,
x
)
\displaystyle \ \ \ \ \ \ = \sum_{(x, t) \in E} f(x, t) - \sum_{(t, x) \in E} f(T, x)
=(x,t)∈E∑f(x,t)−(t,x)∈E∑f(T,x)
我们所说的最大流就是最大可行流的流量
max
∣
f
∣
\max |f|
max∣f∣
残留网络
对于任意一个可行流
f
f
f,都有一个与之对应的残留网络
G
f
=
{
V
G
f
,
E
G
f
}
G_f = \{V_{G_f}, E_{G_f}\}
Gf={VGf,EGf}。
V
G
f
=
V
G
,
E
G
f
=
E
G
+
E
G
V_{G_f} = V_G, E_{G_f} = E_G + E_G
VGf=VG,EGf=EG+EG 的所有反向边。
我们把残留网络中的边
(
u
,
v
)
(u, v)
(u,v) 与原网络相比的正向边的容量命名为
c
′
(
u
,
v
)
c'(u, v)
c′(u,v),流量反向边容量
c
′
′
(
v
,
u
)
c''(v, u)
c′′(v,u),流量
f
′
′
(
v
,
u
)
f''(v, u)
f′′(v,u)。
c
′
(
u
,
v
)
=
c
(
u
,
v
)
−
f
(
u
,
v
)
\displaystyle c'(u, v) = c(u, v) - f(u, v)
c′(u,v)=c(u,v)−f(u,v)
c
′
′
(
v
,
u
)
=
f
(
u
,
v
)
\displaystyle c''(v, u) = f(u, v)
c′′(v,u)=f(u,v)
需要残留网络是因为有时需要退流,如下图。
这是一个可行流,但不是最大流,但是我们如果把绿色部分的流量加
1
1
1,那么中间的一条竖着的边的流量就会被抵消,这样就得到了最大流。由此可见,可能会出现退流的情况,所以我们处理时用残留网络。
现在我们有两个网络,原网络和残留网络,如果把原网络可行流
f
1
f_1
f1 和残留网络可行流
f
2
f_2
f2相加,得到一个新的流
f
3
f_3
f3, 会发生什么。
我们先定义一下两个网络的可行流的加法,对于
f
2
f_2
f2 中的正向边,把它的流量加在
f
1
f_1
f1 上,反向边减去(因为反向边是退流)。
定义完加法后,我们发现
f
3
f_3
f3 也是一个原图的可行流,且
∣
f
1
∣
+
∣
f
2
∣
=
∣
f
3
∣
|f_1| + |f_2| = |f_3|
∣f1∣+∣f2∣=∣f3∣,原因如下。
c
′
(
u
,
v
)
=
c
(
u
,
v
)
−
f
(
u
,
v
)
\displaystyle c'(u, v) = c(u, v) - f(u, v)
c′(u,v)=c(u,v)−f(u,v)
c
′
′
(
v
,
u
)
=
f
(
u
,
v
)
\displaystyle c''(v, u) = f(u, v)
c′′(v,u)=f(u,v)
0
≤
f
(
u
,
v
)
≤
c
(
u
,
v
)
\displaystyle 0 \le f(u, v) \le c(u, v)
0≤f(u,v)≤c(u,v)
先考虑正向边。
0
≤
f
′
(
u
,
v
)
≤
c
′
(
u
,
v
)
=
c
(
u
,
v
)
−
f
(
u
,
v
)
0 \le f'(u, v) \le c'(u, v) = c(u, v) - f(u, v)
0≤f′(u,v)≤c′(u,v)=c(u,v)−f(u,v)
0
≤
f
(
u
,
v
)
+
f
′
(
u
,
v
)
≤
c
(
u
,
v
)
0 \le f(u, v) + f'(u, v) \le c(u, v)
0≤f(u,v)+f′(u,v)≤c(u,v)
再考虑反向边
0
≤
f
′
′
(
v
,
u
)
≤
c
′
′
(
v
,
u
)
=
f
(
u
,
v
)
≤
c
(
u
,
v
)
0 \le f''(v, u) \le c''(v, u) = f(u, v) \le c(u, v)
0≤f′′(v,u)≤c′′(v,u)=f(u,v)≤c(u,v)
0
≤
f
(
u
,
v
)
−
f
′
′
(
v
,
u
)
≤
c
(
u
,
v
)
0 \le f(u, v) - f''(v, u) \le c(u, v)
0≤f(u,v)−f′′(v,u)≤c(u,v)
由此可见,
f
3
f_3
f3 满足流量限制。
原网络流入 = 流出,残留网络流入 = 流出。对于每一个左面的边,如果是正向边,就把原网络进入的点加上这条边的流量,把进入的流量加上了这条边的流量;如果是反向边,就把原网络出去的边减去流量,把流出的流量减去了这条边的流量,减少了减去的流量,也相当于加上这条边的流量。相同加相同结果也相同,所以
f
3
f_3
f3 满足流量守恒。
所以
f
3
f_3
f3 满足容量限制和流量守恒,
f
3
f_3
f3 是原图的可行流证明完毕。
有一个公式,
∣
f
1
+
f
2
∣
=
∣
f
1
∣
+
∣
f
2
∣
|f_1 + f_2| = |f_1| + |f_2|
∣f1+f2∣=∣f1∣+∣f2∣。这个很好理解,原网络净往外流出的流量加上残留网络净往外流出的流量就等于净往外流出的流量。所以
∣
f
1
∣
+
∣
f
2
∣
=
∣
f
3
∣
|f_1| + |f_2| = |f_3|
∣f1∣+∣f2∣=∣f3∣ 证明完毕。
原命题证明完毕。
由此可以推出,如果当前可行流的残留网络存在流量 > 0 > 0 >0 的可行流,则当前可行流一定不是最大流。但并不能保证不存在流量 > 0 > 0 >0 的可行流,当前可行流就一定是最大流。
增广路径
在残留网络里,从原点出发,沿着容量大于 0 0 0 的边走,最终到达汇点的路径(一般情况下无环)。
我们发现,增广路径是一个可行流,并且它的流量 > 0 > 0 >0(沿着容量 > 0 > 0 >0 的边走)。增广路是一种最简单的流量 > 0 > 0 >0 的可行流。
我们现在需要证明,不存在流量 > 0 > 0 >0 的增广路,当前可行流就一定是最大流。
割
把一个流网络分成两个集合 S S S 和 T T T,使得 S ⋃ T = V , S ⋂ T = ϕ , s ∈ S , t ∈ T S \bigcup T = V, S \bigcap T = \phi, s \in S, t \in T S⋃T=V,S⋂T=ϕ,s∈S,t∈T,分出的结果就是这个流网络的割。
割的容量
c
(
S
,
T
)
=
∑
u
∈
S
∑
v
,
∈
T
c
(
u
,
v
)
\displaystyle c(S, T) = \sum_{u \in S} \sum_{v, \in T} c(u, v)
c(S,T)=u∈S∑v,∈T∑c(u,v)。
最小割指容量最小的割。
割的流量
f
(
S
,
T
)
=
∑
u
∈
S
∑
v
∈
T
f
(
u
,
v
)
−
∑
v
∈
S
∑
u
∈
T
f
(
v
,
u
)
\displaystyle f(S, T) = \sum_{u \in S} \sum_{v \in T} f(u, v) - \sum_{v \in S} \sum_{u \in T} f(v, u)
f(S,T)=u∈S∑v∈T∑f(u,v)−v∈S∑u∈T∑f(v,u)
第一个性质,
∀
[
S
,
T
]
∀
f
f
(
S
,
T
)
≤
c
(
S
,
T
)
\forall [S, T] \forall f f(S, T) \le c(S, T)
∀[S,T]∀ff(S,T)≤c(S,T)。
这个很好证明,因为每一个
f
f
f 都
>
0
> 0
>0 所以
f
(
S
,
T
)
=
∑
u
∈
S
∑
v
∈
T
f
(
u
,
v
)
−
∑
v
∈
S
∑
u
∈
T
f
(
v
,
u
)
\displaystyle f(S, T) = \sum_{u \in S} \sum_{v \in T} f(u, v) - \sum_{v \in S} \sum_{u \in T} f(v, u)
f(S,T)=u∈S∑v∈T∑f(u,v)−v∈S∑u∈T∑f(v,u)
≤
∑
u
∈
S
∑
v
∈
T
f
(
u
,
v
)
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \displaystyle \le \sum_{u \in S} \sum_{v \in T} f(u, v)
≤u∈S∑v∈T∑f(u,v)
≤
∑
u
∈
S
∑
v
∈
T
c
(
u
,
v
)
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \displaystyle \le \sum_{u \in S} \sum_{v \in T} c(u, v)
≤u∈S∑v∈T∑c(u,v)
第二个性质,
∀
[
S
,
T
]
∀
f
f
(
S
,
T
)
=
∣
f
∣
\forall [S, T] \forall f f(S, T) = |f|
∀[S,T]∀ff(S,T)=∣f∣。
这个也很好想,从
s
s
s 流到
t
t
t,一定是从两个集合中间的边流过去的,但还需要证明。
定义任意两个集合的流量
f
(
X
,
Y
)
=
∑
u
∈
X
∑
v
∈
Y
f
(
u
,
v
)
−
∑
v
∈
X
∑
u
∈
Y
−
f
(
u
,
v
)
\displaystyle f(X, Y) = \sum_{u \in X} \sum_{v \in Y} f(u, v) - \sum_{v \in X} \sum_{u \in Y} - f(u, v)
f(X,Y)=u∈X∑v∈Y∑f(u,v)−v∈X∑u∈Y∑−f(u,v)。
则
f
(
X
,
Y
)
=
−
f
(
Y
,
X
)
f(X, Y) = -f(Y, X)
f(X,Y)=−f(Y,X)
f
(
X
,
X
)
=
0
f(X, X) = 0
f(X,X)=0
∀
Z
∀
X
⋂
Y
=
/
ϕ
f
(
Z
,
X
⋃
Y
)
=
f
(
Z
,
X
)
+
f
(
Z
,
Y
)
\forall Z \forall X \bigcap Y {=}\mathllap{/\,} \phi f(Z, X \bigcup Y) = f(Z, X) + f(Z, Y)
∀Z∀X⋂Y=/ϕf(Z,X⋃Y)=f(Z,X)+f(Z,Y)
∀
Z
∀
X
⋂
Y
=
/
ϕ
f
(
X
⋃
Y
,
Z
)
=
f
(
X
,
Z
)
+
f
(
Y
,
Z
)
\forall Z \forall X \bigcap Y {=}\mathllap{/\,} \phi f(X \bigcup Y, Z) = f(X, Z) + f(Y, Z)
∀Z∀X⋂Y=/ϕf(X⋃Y,Z)=f(X,Z)+f(Y,Z)
由此可得
f
(
S
,
V
)
=
f
(
S
,
S
)
+
f
(
S
,
T
)
f(S, V) = f(S, S) + f(S, T)
f(S,V)=f(S,S)+f(S,T)
f
(
S
,
V
)
=
f
(
S
,
T
)
f(S, V) = f(S, T)
f(S,V)=f(S,T)
f
(
{
s
}
,
V
)
+
f
(
S
−
{
s
}
,
V
)
=
f
(
S
,
T
)
f(\{s\}, V) + f(S - \{s\}, V) = f(S, T)
f({s},V)+f(S−{s},V)=f(S,T)
设
S
′
=
S
−
{
s
}
S' = S - \{s\}
S′=S−{s}
则
f
(
S
′
,
V
)
=
∑
u
∈
S
′
∑
v
∈
V
f
(
u
,
v
)
−
∑
u
∈
S
′
∑
v
∈
V
f
(
v
,
u
)
=
0
\displaystyle f(S', V) = \sum_{u \in S'} \sum_{v \in V} f(u, v) - \sum_{u \in S'} \sum_{v \in V} f(v, u) = 0
f(S′,V)=u∈S′∑v∈V∑f(u,v)−u∈S′∑v∈V∑f(v,u)=0
f
(
{
s
}
,
V
)
=
f
(
S
,
T
)
f(\{s\}, V) = f(S, T)
f({s},V)=f(S,T)
f
(
S
,
T
)
=
∣
f
∣
f(S, T) = |f|
f(S,T)=∣f∣
证明完毕
因此, ∀ [ S , T ] ∀ f ∣ f ∣ ≤ c ( S , T ) \forall [S, T] \forall f |f| \le c(S, T) ∀[S,T]∀f∣f∣≤c(S,T),因此最大流 ≤ \le ≤ 最小割。
最大流最小割定理
最大流最小割定理指
- f f f 是最大流
- G f G_f Gf 中不存在增广路
- ∃ [ S , T ] ∣ f ∣ = c ( S , T ) \exist [S, T] |f| = c(S, T) ∃[S,T]∣f∣=c(S,T)
三者等价
只需要证明1推2,2推3,3推1即可证明定理
先证1推2
采用反证法,如果
f
f
f 是最大流,且
G
f
G_f
Gf 中存在增广路,则将增广路加在原可行流上,得到一个更大的流,与
f
f
f 是最大流矛盾,证明完毕
再证3推1
∣
f
∣
=
C
(
S
,
T
)
|f| = C(S, T)
∣f∣=C(S,T)
最大流
≤
c
(
S
,
T
)
\le c(S, T)
≤c(S,T)
最大流
≥
∣
f
∣
\ge |f|
≥∣f∣
∣
f
∣
=
c
(
S
,
T
)
≤
|f| = c(S, T) \le
∣f∣=c(S,T)≤ 最大流
最大流
=
∣
f
∣
= |f|
=∣f∣
证明完毕
最后证2推3
采用构造,借助残留网络构造一个割
S
S
S:从
s
s
s 沿着非负边走走到的所有点。
T
T
T:
V
−
S
V - S
V−S
首先,由于没有增广路,所以
s
s
s 和
t
t
t 是不会在一个集合里的,所以这是一个合法的割。
其次,由于残留网络中所有
S
S
S 到
T
T
T 的边都是
0
0
0(因为如果不是的话就不符合
S
S
S 的定义了),所以原网络中
∃
x
∈
S
∃
y
∈
T
f
(
x
,
y
)
=
c
(
x
,
y
)
,
f
(
y
,
x
)
=
0
\exist x \in S \exist y \in T f(x, y) = c(x, y),f(y, x) = 0
∃x∈S∃y∈Tf(x,y)=c(x,y),f(y,x)=0,因为如果
f
(
x
,
y
)
=
/
c
(
x
,
y
)
f(x, y) {=}\mathllap{/\,} c(x, y)
f(x,y)=/c(x,y),则残留网络中
f
′
(
x
,
y
)
=
/
0
f'(x, y) {=}\mathllap{/\,} 0
f′(x,y)=/0,如果
f
(
y
,
x
)
=
/
0
f(y, x) {=}\mathllap{/\,} 0
f(y,x)=/0,则
f
′
’
(
x
,
y
)
=
/
0
f'’(x, y) {=}\mathllap{/\,} 0
f′’(x,y)=/0。
所以 $|f| = f(S, T) =
f
(
S
,
T
)
=
∑
u
∈
S
∑
v
∈
T
f
(
u
,
v
)
−
∑
v
∈
S
∑
u
∈
T
f
(
v
,
u
)
\displaystyle f(S, T) = \sum_{u \in S} \sum_{v \in T} f(u, v) - \sum_{v \in S} \sum_{u \in T} f(v, u)
f(S,T)=u∈S∑v∈T∑f(u,v)−v∈S∑u∈T∑f(v,u) =
c
(
S
,
T
)
=
∑
u
∈
S
∑
v
,
∈
T
c
(
u
,
v
)
\displaystyle c(S, T) = \sum_{u \in S} \sum_{v, \in T} c(u, v)
c(S,T)=u∈S∑v,∈T∑c(u,v) = c(S, T)
证明完毕
EK算法
EK算法的实现过程如下:
- 找增广路
- 把路径上的流量更新
- 重复执行,知道没有增广路为止
注意,在求最大流时维护的是残留网络,而不是原网络
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010, M = 20010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], pre[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
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 t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (!st[ver] && f[i])
{
st[ver] = true;
d[ver] = min(d[t], f[i]);
pre[ver] = i;
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int EK()
{
int r = 0;
while (bfs())
{
r += 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 r;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", EK());
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/403258/
来源:AcWing
Dinic算法
Dinic算法是EK算法的一个优化,它一次找多条增广路,为了避免找增广路时死循环,我们给原网络建分层图,每次只能走到下一层。
Dinic是先bfs建分层图,再dfs找增广路。有一个优化——当前弧优化(代码里的cur )。它在一条边的流量更新完时更新,下一次枚举就能直接跳过之前更新完的边。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, M = 200010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
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 t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
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 ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[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()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/412368/
来源:AcWing
最大流的应用方法
在解决最大流问题时,通常是把原问题转化成最大流问题,证明他们之间的数量关系,再用网络流算法求解。
最小割
∃
[
S
,
T
]
∣
f
∣
=
c
(
S
,
T
)
\exist [S, T] |f| = c(S, T)
∃[S,T]∣f∣=c(S,T) 时
∣
f
∣
=
|f| =
∣f∣= 最小割,因为
最小割
≤
c
(
S
,
T
)
=
∣
f
∣
\le c(S, T) = |f|
≤c(S,T)=∣f∣
又因为
f
f
f 是最大流时
∃
[
S
,
T
]
∣
f
∣
=
c
(
S
,
T
)
\exist [S, T] |f| = c(S, T)
∃[S,T]∣f∣=c(S,T),所以
f
f
f 是最大流时
∣
f
∣
=
|f| =
∣f∣= 最小割,所以最大流 = 最小割。
直接跑最大流即可求出最小割。
代码(EK实现)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010, M = 20010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], pre[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
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 t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (!st[ver] && f[i])
{
st[ver] = true;
d[ver] = min(d[t], f[i]);
pre[ver] = i;
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int EK()
{
int r = 0;
while (bfs())
{
r += 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 r;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", EK());
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/403258/
来源:AcWing
代码(Dinic实现)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, M = 200010, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
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 t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
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 ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[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()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dinic());
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/412368/
来源:AcWing