网络流
1. 网络流原理
原理
1.1 流网络
-
是一个有向图
G=(V, E)
,可以存在环。该有向图中存在一个源点s和一个汇点t。边上的权重被称为容量c
。 -
如下图就是一个流网络:
1.2 可行流
-
可行流
f
表示满足一定条件的网络流。需要满足如下两个条件:-
(1)容量限制: 0 ≤ f ( u , v ) ≤ c ( u , v ) 0 \le f(u, v) \le c(u, v) 0≤f(u,v)≤c(u,v),其中 f ( u , v ) 、 c ( u , v ) f(u, v)、c(u, v) f(u,v)、c(u,v) 分别表示从
u
到v
的流量和容量。 -
(2)流量守恒: ∀ x ∈ V / { s , t } , ∑ ( u , x ) ∈ E f ( u , x ) = ∑ ( x , v ) ∈ E f ( x , v ) \forall x \in V/\{s, t\}, \sum _{(u, x) \in E} f(u, x) = \sum _{(x, v) \in E} f(x, v) ∀x∈V/{s,t},∑(u,x)∈Ef(u,x)=∑(x,v)∈Ef(x,v),即除了源点和汇点之外,流入任意点
x
的流量等于流出该点的流量。
-
-
可行流
f
的大小用|f|
表示,表示从源点s
流向其他点的流量。公式表示为:
∣ f ∣ = ∑ ( s , v ) ∈ E f ( s , v ) − ∑ ( v , s ) ∈ E f ( v , s ) |f| = \sum _{(s, v) \in E} f(s, v) - \sum _{(v, s) \in E} f(v, s) ∣f∣=(s,v)∈E∑f(s,v)−(v,s)∈E∑f(v,s)
一般来说,后一项为0。
-
最大流:指的是网络
G
的最大可行流(即对应的|f|
最大)。 -
如下图红色标出的是一个可行流,同时也是一个最大流:
- 一个网络可能会有很多可行流(甚至无穷多个),但是最大流只有一个。
1.3 残留网络
-
残留网络是针对图
G
的某一个可行流f
而言的,记为 G f G_f Gf 。残留网络和可行流是一一对应的。 -
残留网络记为 G f = ( V f , E f ) G_f = (V_f, E_f) Gf=(Vf,Ef),其中 V f = V V_f = V Vf=V, E f = E ∪ E 对 应 的 所 有 反 向 边 E_f = E \cup E对应的所有反向边 Ef=E∪E对应的所有反向边。残留网络也是一个流网络,各个边对应的容量定义如下:
c ′ ( u , v ) = { c ( u , v ) − f ( u , v ) ( u , v ) ∈ E f ( v , u ) ( v , u ) ∈ E c'(u, v) = \begin{cases} c(u, v) - f(u, v) \quad \quad (u, v) \in E \\ f(v, u) \quad \quad (v, u) \in E \end{cases} c′(u,v)={c(u,v)−f(u,v)(u,v)∈Ef(v,u)(v,u)∈E
也就是说残留网络的反向边容量 等于 原图的正向边的流量,残留网络的正向边的容量 等于 原图边的容量减去流量。
- 上面例子可行流
f
对应的残留网络如下图(蓝边是原图中存在的边,红边是反向边):
-
给定一个流网络
G
,对于G
的一个可行流f
,我们可以得到一个残留网络 G f G_f Gf,若该残留网络存在一个可行流f'
,则有:f+f'
是原图G
的一个可行流。且|f+f'| = |f| + |f'|
。 -
这里定义的两个可行流相加:每条边对应的权值相加,如果
f'
对应边的方向和f
中对应边的方法相同,则将f'
中对应边累加到f
边中;如果方向不同,则让f
中对应的边减去f'
中对应边的权值。 -
上述结论的证明:需要证明两点:(1)容量限制;(2)流量守恒;
-
(1)容量限制:
-
如果两个可行流中边的方向相同,则 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);(此时原图中的边方向是
(u, v)
); -
如果两个可行流中边的方向不同,则 0 ≤ f ′ ( u , v ) ≤ c ′ ( u , v ) = f ( v , u ) ≤ c ( v , u ) 0 \le f'(u, v) \le c'(u, v) = f(v, u) \le c(v, u) 0≤f′(u,v)≤c′(u,v)=f(v,u)≤c(v,u),所以 0 ≤ f ( v , u ) − f ′ ( u , v ) ≤ c ( v , u ) 0 \le f(v, u) - f'(u, v) \le c(v, u) 0≤f(v,u)−f′(u,v)≤c(v,u);(此时原图中的边方向是
(v, u)
);
-
-
(2)流量守恒:考虑
f
或者f'
中的某个点,因为在两个图中原来是流量守恒的,因此相加后流量也是守恒的。 -
最后还要证明
|f+f'| = |f| + |f'|
,根据定义:可行流的流量指的是从源点流出的流量大小,因此等式成立。
1.4 增广路径
-
在残留网络中,从源点
s
出发,沿着容量大于零的边,如果能到达汇点t
,那么这条路径就被称为增广路径。 -
上面的例子中对应的残留网络不存在增广路径。下面的例子存在一个增广路径:
-
一个增广路径其实就是指:一个最简单的流量大于零的可行流。
-
有如下结论:如果可行流
f
是最大流,则f
对应的残留网络中不存在增广路径。 -
可以使用反证法证明:如果可行流
f
是最大流,并且f
对应的残留网络中存在增广路径。因为增广路径本质上是残留网络中一个流量大于零的可行流,记为f'
,由上面的讲解可知f+f'
是原图的一个更大的可行流,因此推出f
不是最大流,矛盾。所以假设不成立,原结论成立。 -
那现在存在一个问题:上述结论反过来是否成立呢?即:若原图的一个可行流
f
对应的残留网络中不存在增广路径,是否可以推出f
是最大流呢?结论是可以推出。这个结论的证明就需要借助下面割的概念。
1.5 割
-
定义:将点集
V
分成不重不漏的两个子集S、T
,即 S ∩ T = ∅ , S ∪ T = V S \cap T = \empty, S \cup T = V S∩T=∅,S∪T=V ,且 s ∈ S , t ∈ T s \in S, t \in T s∈S,t∈T,则[S、T]
称为原图的一个割。 -
割的容量:原图中所有从
S
指向T
的容量之和,即 c ( S , T ) = ∑ u ∈ S ∑ v ∈ T c ( u , v ) c(S, T) = \sum _{u \in S} \sum _{v \in T} c(u, v) c(S,T)=∑u∈S∑v∈Tc(u,v)。最小割:指的是容量最小对应的割。 -
割的流量:原图对应的可行流
f
中,从S
到T
的流量之和 减去 从T
到S
的流量之和,即 f ( S , T ) = ∑ u ∈ S ∑ v ∈ T f ( u , v ) − ∑ v ∈ S ∑ u ∈ T f ( u , v ) f(S, T) = \sum _ {u \in S} \sum _ {v \in T} f(u, v) - \sum _ {v \in S} \sum _ {u \in T} f(u, v) f(S,T)=∑u∈S∑v∈Tf(u,v)−∑v∈S∑u∈Tf(u,v)。
- 对于任意原图的可行流
f
,任意割[S, T]
,有|f| = f(S, T)
。证明如下:
首先由如下前置知识,对于
f
(
X
,
Y
)
=
∑
u
∈
X
∑
v
∈
Y
f
(
u
,
v
)
−
∑
v
∈
X
∑
u
∈
Y
f
(
u
,
v
)
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∈Yf(u,v)−∑v∈X∑u∈Yf(u,v):
f
(
X
,
Y
)
=
−
f
(
Y
,
X
)
f
(
X
,
X
)
=
0
f
(
Z
,
X
∪
Y
)
=
f
(
Z
,
X
)
+
f
(
Z
,
Y
)
前
提
:
X
∩
Y
=
∅
f
(
X
∪
Y
,
Z
)
=
f
(
X
,
Z
)
+
f
(
Y
,
Z
)
前
提
:
X
∩
Y
=
∅
f(X, Y) = -f(Y, X) \\\\ f(X, X) = 0 \\\\ f(Z, X \cup Y) = f(Z, X) + f(Z, Y) \quad \quad 前提:X \cap Y = \empty \\\\ f(X \cup Y, Z) = f(X, Z) + f(Y, Z) \quad \quad 前提:X \cap Y = \empty
f(X,Y)=−f(Y,X)f(X,X)=0f(Z,X∪Y)=f(Z,X)+f(Z,Y)前提:X∩Y=∅f(X∪Y,Z)=f(X,Z)+f(Y,Z)前提:X∩Y=∅
因为
S
∪
T
=
V
,
S
∩
T
=
∅
S \cup T = V, S \cap T = \empty
S∪T=V,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,S)+f(S,T),所以:
f
(
S
,
T
)
=
f
(
S
,
V
)
−
f
(
S
,
S
)
=
f
(
S
,
V
)
=
f
(
{
s
}
,
V
)
+
f
(
S
−
{
s
}
,
V
)
=
f
(
{
s
}
,
V
)
+
f
(
S
′
,
V
)
=
f
(
{
s
}
,
V
)
=
∣
f
∣
f(S, T) = f(S, V) - f(S, S) \\\\ = f(S, V) \\\\ = f(\{s\}, V) + f(S-\{s\}, V) \\\\ = f(\{s\}, V) + f(S', V) \\\\ = f(\{s\}, V) = |f|
f(S,T)=f(S,V)−f(S,S)=f(S,V)=f({s},V)+f(S−{s},V)=f({s},V)+f(S′,V)=f({s},V)=∣f∣
上式中
S
′
=
S
−
{
s
}
S'= S-\{s\}
S′=S−{s},因为S'
中不包含源点和汇点,因为流量守恒,所以
f
(
S
′
,
V
)
=
0
f(S', V) = 0
f(S′,V)=0。形式化证明:
f
(
S
′
,
V
)
=
∑
u
∈
S
′
∑
v
∈
V
f
(
u
,
v
)
−
∑
u
∈
S
′
∑
v
∈
V
f
(
v
,
u
)
=
∑
u
∈
S
′
(
∑
v
∈
V
f
(
u
,
v
)
−
∑
v
∈
V
f
(
v
,
u
)
)
=
0
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) \\\\ = \sum _ {u \in S'} \Big( \sum _ {v \in V}f(u, v) - \sum _ {v \in V} f(v, u) \Big) = 0
f(S′,V)=u∈S′∑v∈V∑f(u,v)−u∈S′∑v∈V∑f(v,u)=u∈S′∑(v∈V∑f(u,v)−v∈V∑f(v,u))=0
- 对于任意原图的可行流
f
,任意割[S, T]
,有|f| <= c(S, T)
。证明如下:
∣ f ∣ = f ( S , T ) = ∑ u ∈ S ∑ v ∈ T f ( u , v ) − ∑ v ∈ S ∑ u ∈ T f ( u , v ) ≤ ∑ u ∈ S ∑ v ∈ T f ( u , v ) ≤ ∑ u ∈ S ∑ v ∈ T c ( u , v ) = c ( S , T ) |f| = f(S, T) = \sum _ {u \in S} \sum _ {v \in T} f(u, v) - \sum _ {v \in S} \sum _ {u \in T} f(u, v) \\\\ \le \sum _ {u \in S} \sum _ {v \in T} f(u, v) \\\\ \le \sum _ {u \in S} \sum _ {v \in T} c(u, v) = c(S, T) ∣f∣=f(S,T)=u∈S∑v∈T∑f(u,v)−v∈S∑u∈T∑f(u,v)≤u∈S∑v∈T∑f(u,v)≤u∈S∑v∈T∑c(u,v)=c(S,T)
- 推论:因为任意可行流小于等于任意割的容量,因此有最大流<=最小割。
最大流最小割定理
-
下面三句话是等价的:
-
(1)可行流
f
是最大流; -
(2)可行流
f
的残留网络中不存在增广路; -
(3)存在某个割
[S, T]
,有|f| = c(S, T)
。
-
-
接着是上述定理的证明,这里证明(1)可以推出(2),(2)可以推出(3),(3)可以推出(1)即可。
(1)->(2)
:
- 1.4中已经使用反证法证明了。
(3)->(1)
:
-
已知:存在某个割
[S, T]
,有|f| = c(S, T)
,需要证明f
是最大流。 -
最大流 <=
c(S, T) = |f|
,又因为 最大流 >=|f|
,因此f
是最大流。 -
另外最大流等于最小割,这是因为:最小割 <=
c(S, T)
=|f|
<= 最大流,且 最小割 >= 最大流。
(2)->(3)
:
-
我们的思路是构造一个割,使得割的容量等于可行流的流量。构造方式如下:
-
S:在 G f G_f Gf 中,从源点
s
出发,沿着容量大于0的边能够走到的所有点的集合。因为不存在增广路,所以汇点t
一定不属于S。 -
T:
T = V - S
。汇点t
属于T。 -
此时
[S, T]
是一个割,下面要验证从该可行流f
对应的残留网络构造的割的容量是否等于|f|
。考虑原图中S指向T的边,则这些边上的流量一定等于边上的容量,即f(x, y) = c(x, y)
, x ∈ S , y ∈ T x \in S, y \in T x∈S,y∈T,因为这些边在残留网络中一定为0;考虑从T指向S的边,这些边上的流量一定为0,如果不为0的话,意味着对应的残留网络中会有一条从S指向T的反向边,且边权不为0,和我们的构造方式矛盾。如下图:
所以有:
∣
f
∣
=
f
(
S
,
T
)
=
∑
u
∈
S
∑
v
∈
T
f
(
u
,
v
)
−
∑
u
∈
S
∑
v
∈
T
f
(
v
,
u
)
=
∑
u
∈
S
∑
v
∈
T
f
(
u
,
v
)
=
∑
u
∈
S
∑
v
∈
T
c
(
u
,
v
)
=
c
(
S
,
T
)
|f| = f(S, T) \\\\ = \sum _ {u \in S} \sum _ {v \in T} f(u, v) - \sum _ {u \in S} \sum _ {v \in T} f(v, u) \\\\ = \sum _ {u \in S} \sum _ {v \in T} f(u, v) \\\\ = \sum _ {u \in S} \sum _ {v \in T} c(u, v) \\\\ = c(S, T)
∣f∣=f(S,T)=u∈S∑v∈T∑f(u,v)−u∈S∑v∈T∑f(v,u)=u∈S∑v∈T∑f(u,v)=u∈S∑v∈T∑c(u,v)=c(S,T)
证毕!
算法思想:FF
while (存在增广路) {
(1) 找增广路;
(2) 更新残留网络;
}
-
我们维护的是残留网络,每次在残留网络中如果能够找到一条增广路径的话,如果该增广路径上边权最小值为
k
,则让该路径上的所有正向边-k
,所有反向边+k
,可行流的流量+k
。 -
为了方便维护,我们让残留网络的正向边、反向边编号分别为
(0, 1)、(2, 3)、...
,这样,当我们知道其中的一条边i
,则另一条边为i^1
。 -
常用的网络流算法有:
-
(1)EK算法,时间复杂度: O ( n × m 2 ) O(n \times m ^ 2) O(n×m2);
-
(2)dinic算法,时间复杂度: O ( n 2 × m ) O(n^2 \times m) O(n2×m);
-
(3)ISAP、HLPP等。
-
-
实际使用中我们并不要考虑时间复杂度,一般来说,如果点数+边数不超过一万,可以使用EK算法;如果点数+边数不超过一万,可以使用dinic算法。
2. AcWing上的网络流题目
AcWing 2171. EK求最大流
问题描述
- 问题链接:AcWing 2171. EK求最大流
分析
-
最大流模板题。
-
每次使用
bfs
算法求解残留网络中的增广路即可。
代码
- C++
#include <iostream>
#include <cstring>
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; // f: 记录边权(容量)
int q[N]; // bfs使用的队列
int d[N]; // 从S到当前点边权最小值
int 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++; // 加入(a, b, c)
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++; // 加入(b, a, 0)
}
// 判断残留网络是否存在增广路径 以及 求解增广路径
bool bfs() {
memset(st, 0, sizeof st);
int hh = 0, tt = -1;
q[++tt] = 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]) { // 未被遍历过,并且边权不为0
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]) { // e[pre[i] ^ 1]是i的前驱点
f[pre[i]] -= d[T]; // 正向边减去k=d[T]
f[pre[i] ^ 1] += d[T]; // 反向边加上k=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;
}
标签:可行,增广,int,sum,网络,残留 来源: https://blog.csdn.net/weixin_42638946/article/details/120508463
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。