问题 → \to → 建图转化为网络流的模型 → \to → 建图得到的最优解和原问题是否等价.
网络流是对于一个有向图来说的.
流网络是一个有向图(可以有环).
图是由一些点和有向边组成的,流网络中有两个特殊的点,一个叫源点 s s s,一个叫汇点 t t t.
流网络中的每一条边都有一个属性,叫做流网络的容量 c c c.
图 1中的源点 S S S 应为源点 s s s,汇点 T T T 应为 汇点 t t t.
流网络可以比作一个河流问题:
流网络还可以想象成水管中运输水的模型:
源点有源源不断、无穷多的流量流出,可以想象为一个无穷大的水库.
汇点可以想象为大海,可以容纳无穷多的流量.
整个模型可以看作:
流网络可以记作 G = ( V , E ) G=(V,E) G=(V,E),即点集和边集.
为了简化问题,假定流网络中一定不存在反向边.
图 2如图 2,因为对于有反向边的图,可以在中间添加一个点,等价地变成没有反向边.
如果一条边不存在,则定义其容量 c ( u , v ) = 0 c(u,v)=0 c(u,v)=0.
对于每一个流网络而言,考虑它任意一个可行流.
可行流一般用 f f f 来表示,指指定每一条边的流量,或者每个水管每秒的水流速率,就是每条边自行给它设定一个值,只要满足两个条件,它就是一个可行的流:
注意,上述描述不考虑反向边,因为有些文章说需要满足 3. f ( x , y ) = − f ( y , x ) f(x,y)=-f(y,x) f(x,y)=−f(y,x),但是流量怎么会是负的呢?所以考虑反向边的话,定义不是很自洽.
可行流 f f f 表示的是一种方案.
每秒从源点流到汇点的流量的具体值(速率、流量值),将其定义为 可行流的流量值 ∣ f ∣ |f| ∣f∣,换言之,可以将可行流的流量值定义为:
一般用源点来定义,即每秒从源点流向其他点的总流量 减去 每秒从其他点流入源点的总流量,记为
∣ 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).
一般网络是不会往源点流入的,但可能有些特殊网络是会有这种情况的,为了更具一般性,在定义中都写上.
对于一个流网络来说,会有很多个可行流,每一个可行流都可以求一个流量值,一般说的最大流,指的是流量值最大的可行流,故其可以称为最大可行流.
视全部可行流的流量值为一个集合,求最大流 就可以类比为 在集合中求最大值.
图 3中的源点 S S S 应为源点 s s s,汇点 T T T 应为 汇点 t t t.
如图 3,红色字体为一种可行流,且显然为一种最大可行流,其可行流的流量值为 3 + 6 = 9 3+6=9 3+6=9.
流量是 0 0 0 的话,可以定义成可行流,也可以忽略它.
残留网络是对于流网络的某一条可行流来说的,可行流不同的话,残留网络也不同.
残留网络(残存网络、残量网络)记为 G f G_f Gf ,与可行流一一对应 { f 1 → G f 1 , f 2 → G f 2 . \begin{cases}f_1 \to G_{f_1}, \\ f_2 \to G_{f_2}. \end{cases} {f1→Gf1,f2→Gf2.
残留网络是如何定义的呢?
G f G_f Gf 的点集和原图的点集是一样的,记作 V f = V V_f=V Vf=V;
G f G_f Gf 的边集不仅包含原图的所有边,同时包含 E E E 中的所有反向边,记作 E f = E + E 反 E_f=E+E_反 Ef=E+E反;
残留网络实际上也是一个流网络,其每条边的容量有两种情况:
对于原图中存在的边(正向边), c ′ ( u , v ) c'(u,v) c′(u,v) 定义为可以增加的流量,记作 c ( u , v ) − f ( u , v ) ( u , v ) ∈ E c(u,v)-f(u,v) \quad (u,v) \in E c(u,v)−f(u,v)(u,v)∈E;
对于原图的反向边, c ′ ( u , v ) c'(u,v) c′(u,v) 定义为可以退回去多少,相当于后悔,记作 f ( v , u ) ( v , u ) ∈ E f(v,u) \quad (v,u) \in E f(v,u)(v,u)∈E.
为什么会有退回去的情况?例子如下图 4( 1 1 1 号点为源点 s s s, 4 4 4 号点为汇点 t t t)所示:
如果用 容量为 1 1 1 的这条边的话,流量值并不是最大的,所以应该有退回去的操作.
这也能间接解释残留网络为什么要建反向边.
图 4
残留网络也是一个流网络,所以残留网络也是由可行流的概念,记残留网络 G f G_f Gf 的可行流为 f ′ f' f′,讨论 原网络的可行流 f f f 与其的关系.
定理: f + f ′ f+f' f+f′ 也是原网络 G G G 的一个可行流,进一步可以得到 ∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ |f+f'|=|f|+|f'| ∣f+f′∣=∣f∣+∣f′∣.
注意: f ′ f' f′ 是被 f f f 确定的,并不是说任意一个残留网络的可行流都可以和任意一个可行流相加的,它是有决定关系的.
首先定义两个流量相加(指的是每条边对应相加):
如果残留网络中的边的方向与原网络中边的方向是相同的,就把残留网络中边的流量累加到原网络中;
如果边的方向是相反的,就把残留网络中边的流量 在原网络中减去,可以理解为后悔,退回去一些流量.
f + f ′ f+f' f+f′ 也是原网络 G G G 的一个可行流.
证明
证明是否为可行流,其实就是证其是否满足可行流的两个条件.
0 ≤ f ′ ( u , v ) ≤ c ′ ( u , v ) = c ( u , v ) − f ( u , v ) , 0 ≤ f ′ ( u , v ) ≤ c ( u , v ) − f ( u , v ) , 0 ≤ f ′ ( u , v ) + f ( u , v ) ≤ c ( u , v ) . \begin{aligned} 0 \leq f'(u,v) \leq c'(u,v)=c(u,v)-f(u,v),\\ 0 \leq f'(u,v) \leq c(u,v)-f(u,v), \\ 0\leq f'(u,v)+f(u,v)\leq c(u,v).\end{aligned} 0≤f′(u,v)≤c′(u,v)=c(u,v)−f(u,v),0≤f′(u,v)≤c(u,v)−f(u,v),0≤f′(u,v)+f(u,v)≤c(u,v).
0 ≤ f ′ ( u , v ) ≤ c ′ ( u , v ) = f ( v , u ) ≤ c ( v , u ) , 0 ≤ f ( v , u ) − f ′ ( u , v ) ≤ c ( v , u ) . \begin{aligned}0 \leq f'(u,v) \leq c'(u,v) = f(v,u) \leq c(v,u),\\ 0\leq f(v,u)-f'(u,v) \leq c(v,u).\end{aligned} 0≤f′(u,v)≤c′(u,v)=f(v,u)≤c(v,u),0≤f(v,u)−f′(u,v)≤c(v,u).
与原图相反,对应的原图中的边即为 ( v , u ) (v,u) (v,u).
流量守恒
原网络的可行流满足流量守恒、残留网络的可行流也满足流量守恒,故相加也满足流量守恒.
∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ |f+f'|=|f|+|f'| ∣f+f′∣=∣f∣+∣f′∣ 可以理解为原网络可行流的流量值 加上 残留网络可行流的流量值,残留网络可行流的流量值是在原网络可行流的基础上再往外流出的流量,故可以直接相加.
证毕
在可行流的残留网络中,求得的任何一个流量值大于零的可行流,都可以增加原网络的可行流.
推论:残留网络中如果存在流量值大于零的可行流,则原网络的可行流就是一定不是最大流.
反过来是否成立呢?即 如果残留网络中不存在流量值大于零的可行流,则原网络的可行流一定为最大流 吗?
因为可能存在多峰函数导致其陷入局部最优解的情况,需要进一步的证明,证明见最大流最小割定理.
总结:
在残留网络中,从源点出发,沿着容量大于 0 0 0 的边,如果能够走到汇点,这条路径就被称为增广路径.
增广路径一般指简单路径,即没有环、不自交的一种路径.
如图 6( 1 1 1 号点为源点 s s s, 4 4 4 号点为汇点 t t t),对应 流网络的可行流、可行流的残留网络、残留网络中的增广路径:
增广路径实际上是一个可行流,它实际上是一个最简单的、流量值大于零的可行流.
通过有没有增广路径可以判断出原网络的可行流是否是最大的.
二分图最大匹配的增广路实际上就是增广路径的一种特殊情况.
匈牙利算法本质上也是找增广路径,本质上和最大流算法是一模一样的,它是一种效率较低的最大流算法.
定理:如果对于当前的可行流 f f f 来说,在它的残留网络 G f G_f Gf 中,如果没有增广路径的话,就可以断定 f f f 是一个最大流.
证明见最大流最小割定理.
总结:对于一个流网络 G G G,找到任意一个可行流 f f f,构造出其对应的残留网络 G f G_f Gf,如果 G f G_f Gf 不存在增广路径的话,可以断言 f f f 为最大流.
流网络的割,指的是将点集 V V V 分成两个子集 { S , T , \begin{cases}S, \\T, \end{cases} {S,T, 满足 S ∪ T = V S ∩ T = ∅ S \cup T=V \quad S \cap T= \varnothing S∪T=VS∩T=∅.
还要进一步满足 源点 s ∈ S s \in S s∈S,汇点 t ∈ T t \in T t∈T.
即将点集不重叠地分为两部分,源点 s s s 在 S S S 这一部分,汇点 t t t 在 T T T 这一部分,这样的将点集分割划分的结果称为图的割.
中间所有点可以随便选,不一定联通.
考虑割时,考虑到的边都是原网络中的边,不需要考虑反向边;只有在残留网络中才需要考虑反向边.
割的容量指的是所有从 S S S 指向 T T 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∈T∑c(u,v).
如果一条边不存在的话,可以假设其容量为 0 0 0.
且从 T T T 指向 S S S 的边不算.
最小割指的是割的容量的最小值.
总共有 2 n − 2 2^{n-2} 2n−2 种割(源点 s s s、汇点 t t t 没得选,其他所有点都有分到 S S S 或 T T T 两种选择).
割的流量指的是所有从 S S S 流到 T T T 的流量值 减去 从 T T T 流到 S S S 的流量值,记作:
f ( S , T ) = ∑ u ∈ S ∑ v ∈ T f ( u , v ) − ∑ u ∈ T ∑ v ∈ S f ( u , v ) . f(S,T)=\sum_{u\in S}\sum_{v\in T}f(u,v)-\sum_{u\in T}\sum_{v\in S}f(u,v). f(S,T)=u∈S∑v∈T∑f(u,v)−u∈T∑v∈S∑f(u,v).
可以发现,割的容量和割的流量的定义不是对称的,割的容量只考虑 从 S S S 指向 T T T 的边的容量.
对于流网络的 任意一个割 和 任何一个可行流 ,都会一定存在一个性质: ∀ [ S , T ] ∀ f f ( S , T ) ≤ c ( S , T ) \forall [S,T] \;\forall f\quad f(S,T) \leq c(S,T) ∀[S,T]∀ff(S,T)≤c(S,T),即割的流量小于等于割的容量.
流网络的 可行流 和 割 实际上是两个独立的概念.
只要割固定了,割的容量就固定了;而割的流量是对于流网络的不同的可行流,求的流量是不同的.
证明:
f ( S , T ) = ∑ u ∈ S ∑ v ∈ T f ( u , v ) − ∑ u ∈ T ∑ v ∈ S f ( u , v ) ≤ ∑ u ∈ S ∑ v ∈ T f ( u , v ) ≤ ∑ u ∈ S ∑ v ∈ T c ( u , v ) = c ( S , T ) . f(S,T)=\sum_{u\in S}\sum_{v\in T}f(u,v)-\sum_{u\in T}\sum_{v\in S}f(u,v) \leq \sum_{u\in S}\sum_{v\in T}f(u,v) \leq \sum_{u\in S}\sum_{v\in T}c(u,v)=c(S,T). f(S,T)=u∈S∑v∈T∑f(u,v)−u∈T∑v∈S∑f(u,v)≤u∈S∑v∈T∑f(u,v)≤u∈S∑v∈T∑c(u,v)=c(S,T).
对于任何一个可行流和任何一个割而言,割的流量都等于可行流的流量值,即:
∀ [ S , T ] ∀ f f ( S , T ) = ∣ f ∣ . \forall [S,T] \;\forall f \quad f(S,T)=|f|. ∀[S,T]∀ff(S,T)=∣f∣.
从 S S S 流到 T T T 的流量值 减去 从 T T T 流到 S S S 的流量值 等于 从 s s s 流向 t t t 的净流量值.
证明
首先定义任意两个集合之间的流量值:
f ( X , Y ) = ∑ u ∈ X ∑ v ∈ Y f ( u , v ) − ∑ u ∈ X ∑ v ∈ Y f ( v , u ) f(X,Y)=\sum_{u \in X} \sum_{v \in Y} f(u,v)-\sum_{u \in X}\sum_{v \in Y}f(v,u) f(X,Y)=u∈X∑v∈Y∑f(u,v)−u∈X∑v∈Y∑f(v,u)
性质1. f ( X , Y ) = − f ( Y , X ) f(X,Y)=-f(Y,X) f(X,Y)=−f(Y,X),显然,变符号即可得到.性质2. f ( X , X ) = 0 f(X,X)=0 f(X,X)=0,显然,内部减内部必然为 0 0 0.
性质3. f ( Z , X ∪ Y ) = f ( Z , X ) + f ( Z , Y ) f(Z,X \cup Y)=f(Z,X)+f(Z,Y) f(Z,X∪Y)=f(Z,X)+f(Z,Y),但是要确保 X ∩ Y = ∅ X \cap Y= \varnothing X∩Y=∅,因为 X X X 和 Y Y Y 是没有交集的,所以可以分开来算,当然,反过来也成立,即 f ( X ∪ Y , Z ) = f ( X , Z ) + f ( Y , Z ) f(X \cup Y,Z)=f(X,Z)+f(Y,Z) f(X∪Y,Z)=f(X,Z)+f(Y,Z),变符号即可得到.
根据性质3. 和 S ∪ T = V S ∩ T = ∅ S \cup T=V \quad S\cap T= \varnothing S∪T=VS∩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,T)=f(S,V)-f(S,S)=f(S,V)=f(\{s\},V)+f(S-\{s\},V)=f(\{s\},V). f(S,T)=f(S,V)−f(S,S)=f(S,V)=f({s},V)+f(S−{s},V)=f({s},V).
进一步证明:设 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 ) = ∑ 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'}(\sum_{v \in V} f(u,v) - \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)=u∈S′∑(v∈V∑f(u,v)−v∈V∑f(v,u))=0.
这样,就可以证明 f ( S , T ) = f ( { s } , V ) = ∣ f ∣ f(S,T)=f(\{s\},V)=|f| f(S,T)=f({s},V)=∣f∣.证毕
可以通过以上两点推出:对于流网络的任意一个割和任意一个可行流,都会有 ∣ f ∣ = f ( S , T ) ≤ c ( S , T ) |f|=f(S,T)\leq c(S,T) ∣f∣=f(S,T)≤c(S,T).
也就是说,对于流网络 G G G 来说,它的任意一个可行流的流量值 一定小于等于 它的任意一个割的容量,可以发现,最大可行流的流量值 一定小于等于 割的容量的最小值
所以,可以写成 最大流 ≤ \leq ≤最小割.
对于某一个流网络 G = ( V , E ) G=(V,E) G=(V,E) 来说,以下三个条件是等价的(即其中一个条件成立,其余两个条件也成立):
证明
1推2
反证法:如果 f f f 为最大流,并且 G f G_f Gf 中存在增广路,意味着其存在一个流量值大于零的可行流 ∣ f ′ ∣ > 0 |f'|>0 ∣f′∣>0,就可以发现 ∣ f + f ′ ∣ = ∣ f ∣ + ∣ f ′ ∣ > ∣ f ∣ |f+f'|=|f|+|f'|>|f| ∣f+f′∣=∣f∣+∣f′∣>∣f∣,矛盾.
3推1
由于 ∣ f ∣ ≤ c ( S , T ) |f| \leq c(S,T) ∣f∣≤c(S,T),所以 最大流 ≤ \leq ≤ 最小割,因为最大流是所有可行流里的最大值,所以 最大流 ≥ ∣ f ∣ \geq |f| ≥∣f∣,而 ∣ f ∣ = c ( S , T ) ≥ |f|=c(S,T) \geq ∣f∣=c(S,T)≥最大流,所以可以得到 ∣ f ∣ = |f|= ∣f∣=最大流.
同时,可以发现 最小割 ≤ c ( S , T ) = ∣ f ∣ ≤ \leq c(S,T)=|f|\leq ≤c(S,T)=∣f∣≤最大流,就可以推出 最大流 = = =最小割.
2推3
如果对于当前流网络 G G G 的可行流 f f f,其残留网络 G f G_f Gf 不存在增广路径,能不能在原网络中构造一个割,使得割的容量等于可行流的流量值,就可以证明.
S S S:在残留网络当中,从源点 s s s 出发,沿着容量 > 0 >0 >0 的边走,所有能走到的点.
T = V − S T=V-S T=V−S.
找到的割是原网络 G G G 的割,但是 是借助于残留网络 G f G_f Gf 构造的.
对于所有从 S S S 到 T T T 的边,设为 ( x , y ) (x,y) (x,y),则 f ( x , y ) f(x,y) f(x,y)是原网络中的流量,其一定有 f ( x , y ) = c ( x , y ) f(x,y)=c(x,y) f(x,y)=c(x,y).
因为如果 f ( x , y ) < c ( x , y ) f(x,y)
f(x,y)<c(x,y) ,则在残留网络当中, ( x , y ) (x,y) (x,y)的容量应该为 c ( x , y ) − f ( x , y ) > 0 c(x,y)-f(x,y)>0 c(x,y)−f(x,y)>0,则 x x x就不应该属于 S S S,而是属于 T T T,矛盾.
对于所有从 T T T 到 S S S 的边,设为 ( a , b ) (a,b) (a,b) ,一定有 f ( a , b ) = 0 f(a,b)=0 f(a,b)=0.
如果 f ( a , b ) > 0 f(a,b)>0 f(a,b)>0,即从 T T T 向 S S S 流过了流量,则在残留网络中在建边时就会建一条反向边,反向边的容量就应该为 c ′ ( b , a ) = f ( a , b ) > 0 c'(b,a)=f(a,b)>0 c′(b,a)=f(a,b)>0,就意味着 a a a 应该属于 S S S 而不是属于 T T T,矛盾.
这样就会有:
∣ f ∣ = ∑ u ∈ S ∑ v ∈ T f ( u , v ) − ∑ u ∈ S ∑ v ∈ T f ( v , u ) = ∑ u ∈ S ∑ v ∈ T c ( u , v ) = c ( S , T ) . |f|=\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}c(u,v)=c(S,T). ∣f∣=u∈S∑v∈T∑f(u,v)−u∈S∑v∈T∑f(v,u)=u∈S∑v∈T∑c(u,v)=c(S,T).
证毕
到此为止,所有定理证明完毕.
为什么引入割的概念?
因为如果当前可行流为最大流,其残留网络一定没有增广路径;但当前可行流的残留网络没有增广路径,当前可行流是否为最大流,这不是很容易证明,因为这可能是一个极大值但不一定是全局最大值.
为了能够证明当前可行流确实是最大流,必须要引入割的概念.
割实际上是证明最大流的中间过程.
但值得注意的是,有些题目是以最大流为背景的,有些题目是以最小割为背景的,但本质上是一样的.
不管是EK算法、Dinic算法还是ISPA算法,都是算法导论中FF方法的具体实现,核心思想就是FF方法.
FF方法:不断地维护残留网络,不断地在残留网络中寻找增广路径,寻找到增广路径后,去掉增广路径得到新的残留网络,直到残留网络找不到增广路径,就可以断言当前可行流一定是最大流.
EK算法
while()
1.找增广路径 f'
2.更新残留网络 G_f -> G_{f+f'}
}
对于增广路径:
如果找到了一条增广路径,则增加的流量值应该为 最小的一条边的容量 k k k,找到后更新残留网络.
对于残留网络:
如果增广路径经过原网络中的边,流过了 k k k 的流量,则其边的容量 − k -k −k,而其反向边的容量 + k +k +k.
其实就是一个 B F S BFS BFS 记录路径,开一个数组
pre
即可.
如上为EK算法,时间复杂度为 O ( n m 2 ) O(nm^2) O(nm2).
Dinic算法的时间复杂度为 O ( n 2 m ) O(n^2m) O(n2m)(使用了分层图的概念,每次可以寻找多条增广路径).
最大流算法的时间复杂度的上限是十分宽松的,看上去很高,但实际上其运行效率非常高.
EK算法基本上可以处理 1000 ∼ 10000 1000 \sim 10000 1000∼10000 的网络(点数+边数).
Dinic算法处理 10000 ∼ 100000 10000 \sim 100000 10000∼100000 的网络没有问题.
Dinic算法和ISPA算法的区别实际上是不大的,在求最大流时,使用一个即可.
EK算法基本不会用在最大流中,但是最小费用流中,EK算法是一个核心算法.
存图:使用邻接表,更新网络时,需要快速找到某条边的反向边,所以正向边与反向边要成对存储,这样某条边 i i i 的反向边实际上就为 i x o r 1 i \;xor\; 1 ixor1(异或).
原题链接
给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量,边的容量非负。
图中可能存在重边和自环。求从点 S S S 到点 T T T 的最大流。
输入格式
第一行包含四个整数 n , m , S , T n,m,S,T n,m,S,T。
接下来 m m m 行,每行三个整数 u , v , c u,v,c u,v,c,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c。
点的编号从 1 1 1 到 n n n。
输出格式
输出点 S S S 到点 T T T 的最大流。
如果从点 S S S 无法到达点 T T T 则输出 0 0 0。
数据范围
2 ≤ n ≤ 1000 2≤n≤1000 2≤n≤1000,
1 ≤ m ≤ 10000 1≤m≤10000 1≤m≤10000,
0 ≤ c ≤ 10000 0≤c≤10000 0≤c≤10000,
S ≠ T S≠T S=T
输入样例:
7 14 1 7
1 2 5
1 3 6
1 4 5
2 3 2
2 5 3
3 2 2
3 4 3
3 5 3
3 6 7
4 6 5
5 6 1
6 5 1
5 7 8
6 7 7
输出样例:
14
难度: 中等
时/空限制: 1s / 64MB
来源: 模板题,AcWing
算法标签:最大流
Edmonds-Karp
图如果不连通,最大流就是 0 0 0 ,算法也是可以正常运行的.
#include
#include
#include
#include
using namespace std;
const int N=1010,M=20010,INF=1e8; //因为要维护残留网络,故边的数量翻倍
int n,m,S,T;
int h[M],e[M],f[M],ne[M],idx; //f表示容量(文档中对应为c)
int q[N],d[N],pre[N];//q为队列,d代表从起点走到某点时的容量最小值,pre记录前驱点
bool st[N]; //st为了bfs图的遍历判重
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",EK());
return 0;
}
// 运行时间: 44 ms
// 运行空间: 740 KB
最大流只有概念比较抽象,但是做题并不是这样的,做题感觉就和 D P DP DP 差不多了.
最大流的难点不在于证明上,在于建图上.
参考资料:《算法导论》、《最小割模型在信息学竞赛中的应用》(作者:胡伯涛)
本文档基于 AcWing算法进阶课 制作
视频链接:1.1.1 网络流的基本概念 - AcWing
文档版本:
var1.0 完成于2022.01.27.