免责声明:code太难写了,本章就不提供code了,各位自行GPT吧(

基本概念阐述

问题阐述

  • 输入:一个有向有权图 G=(V,E)G=(V,E) ,源节点 ss ,汇点 tt
  • 目标:从 ss 发送尽可能多的水到 tt
  • 约束:
    • 流要小于管道容量

流网络和流

  • 流网络

    • G=(V,E)G=(V,E)​ 是一个有向有权图
      • EV1|E|\ge |V|-1
    • 图中每一条边 (u,v)E(u,v)\in E 有一个非负的容量值 c(u,v)0c(u,v)\ge 0
      • (u,v)E(u,v)\notin E ,则定义 c(u,v)=0c(u,v)=0
    • 源节点 ss ,汇点 tt
  • flowflow

    • 在一个流网络 G=(V,E)G=(V,E) 中,设容量函数为 cc ,源节点 ss ,汇点 tt

    • GG 中的流为一个实值函数 f:V×VRf:V \times V \to R ,满足以下两条性质​:

      • 容量限制:对于所有的节点,0f(u,v)c(u,v)0\le f(u,v)\le c(u,v)
      • 流量守恒:对于除源节点、汇点外(V{s,t}V-\{s,t\})的所有节点,vVf(v,u)=vVf(u,v)\sum_{v \in V}f(v,u)=\sum_{v \in V} f(u,v)
        • 即流入 uu 的总流量等于从 uu​ 流出的总流量
        • 前后两个 vv 一般是不同的,所以我更倾向写成 vVf(v,u)=vVf(u,v)\sum_{v \in V}f(v,u)=\sum_{v' \in V} f(u,v')
        • (u,v)E(u,v)\notin E ,则 f(u,v)=0f(u,v)=0
    • 一个流的值 f|f| 定义为:

      f=vVf(s,v)vVf(v,s)|f|=\sum_{v\in V}f(s,v)-\sum_{v \in V}f(v,s)

      • 表示从源结点流入该节点的总流量减去流入源结点的总流量。
      • 一般而言,没有流入源节点的流,即第二项的值为 00​ ,所以也可以写成:

    f=vVf(s,v)|f|=\sum_{v\in V}f(s,v)

image-20240526145210606 image-20240526145248431

残存网络 Residual GraphResidual\ Graph

image-20240526155445545
  • 残存边 (u,v)(u,v) ,残存容量 cf=c(u,v)f(u,v)c_f=c(u,v)-f(u,v)​​​
  • 回流边 (v,u)(v,u) ,回流值大小 cf=f(v,u)=f(u,v)c_{f'}=f'(v,u)=f(u,v) ——在 FordFulkersonFord-Fulkerson 方法里会用到
    • cf=f(v,u)c_{f'}=\sum f'(v,u) ,也即合并 MergeMerge 操作,后面会给出证明

增广路径 Augmenting pathsAugmenting\ paths

image-20240526163807357
  • 增广路径 pp 是从源节点 ss 到汇点 tt 的一条简单路径
  • 路径 pp瓶颈容量 cf(p)=min{cf(u,v):(u,v)p}c_f(p)=min\{c_f(u,v):(u,v)\in p\}

切割

切割,字面意思,就是把图切割成 nn 份。在本章中,我们主要讨论的是 ST cut\mathcal{S-T}\ cut ,即把图切割成两份。

  • ST cut\mathcal{S-T}\ cut

    • 将节点集合 VV 切割成两个子集:S\mathcal{S}T\mathcal{T}

      • ST=V\mathcal{S} \cup \mathcal{T}=VST=\mathcal{S} \cap \mathcal{T}=\emptyset
      • sSs \in \mathcal{S}tTt \in \mathcal{T}
    • S\mathcal{S}T\mathcal{T} 的二元组 (S,S)(\mathcal{S},\mathcal{S}) 被称为 ST cut\mathcal{S-T}\ cut

    • 定义 ST\mathcal{S-T} 的容量为离开集合 S\mathcal{S}​ 的边的权重之和,如下:

      c(S,T)=uSvTc(u,v)c(\mathcal{S},\mathcal{T})=\sum_{u \in \mathcal{S}}\sum_{v \in \mathcal{T}}c(u,v)

      • 例如下图的 c(S,T)=6c(\mathcal{S},\mathcal{T})=6
    • 定义切割的净流量 f(S,T)f(\mathcal{S},\mathcal{T}) 如下:

      f(S,T)=uSvTf(u,v)uSvTf(v,u)f(\mathcal{S},\mathcal{T})=\sum_{u \in \mathcal{S}}\sum_{v \in \mathcal{T}}f(u,v)-\sum_{u \in \mathcal{S}}\sum_{v \in \mathcal{T}}f(v,u)

      • 例如下图的 f(S,T)=f(v1,v3)+f(v1,v4)+f(v2,v4)f(v4,s)=2+2+24=2f(\mathcal{S},\mathcal{T})=f(v_1,v_3)+f(v_1,v_4)+f(v_2,v_4)-f(v_4,s)=2+2+2-4=2
    • e.g.

    image-20240530100017753 image-20240530100104168
    • 注意,ST cut\mathcal{S-T}\ cut 不唯一
    image-20240530104156572
  • 最小割 MinCutMin-Cut

    • 使容量最小化的切割被称为最小割 MinCutMin-Cut
    • e.g.
    image-20240530105906779 image-20240530105823488

Naive AlgorithmNaive\ Algorithm 引入

Naive 不是一个人名,它的意思是天真的;幼稚的。这里应译为朴素算法

​ 这个算法是初步的算法,并不一定能找到最大流(只能找到阻塞流),只是大多数情况下可以找到最大流,但是这种方法很好理解,并且后面的更优的算法都是以此为基础进行优化的,所以我们先介绍这种算法。

算法实现步骤

  • 创建一个残存图 (residual graph)(residual\ graph) ,初始化残存容量
  • whilewhile 可以找到增广路径 :
    • 找一条增广路径
    • 找出增广路径中的瓶颈容量 xx
    • 更新这条增广路径上每一条边,cf=cfxc_f=c_f-x ,并删除饱和边

图示

  • 初始化
    image-20240526155445545

  • 第一轮循环

    • 随便用什么遍历方法找到一条从 sstt​​​ 的增广路径,找到瓶颈容量,更新这条路径上的每一条边

      image-20240526163807357 image-20240526165117479
    • 此时有两条边的余量为0,说明这两条管道已经饱和了,于是把余量为0的边删除

image-20240526170746298
  • 第二轮循环

    • 同样的操作
image-20240526171700693 image-20240526171744110 image-20240526171908957
  • 第三轮循环
    • 同样的操作
image-20240526172032537 image-20240526172108618 image-20240526172124496
  • 循环结束
    • 用原始图减去最终的残存图,即可得到流量图
    • 实际流量可以从源节点 ss 去进行计算
image-20240526172714170 image-20240526172736018

该简单算法局限性

该算法在寻找路径的时候,如果找到的路径是错误的,则最终找到的有可能不是最大流

image-20240526173253099 image-20240526173332419
image-20240526173406778 image-20240526173424878
image-20240526173650088 image-20240526173725322

image-20240526173606915

阻塞流 blocking flowblocking\ flow

image-20240526173911139
  • 如果从源节点 ss 到汇点 tt ,不能找到更多的流汇入,则该流是阻塞流
  • 最大流是阻塞流

FordFulkersonFord-Fulkerson 算法

​ 上面的简单算法一旦选择了 bad pathbad\ path 后,不能修正,就只能找到阻塞流。

​ 而下面我们介绍的算法,则是以上面的算法为基础进行优化的,FordFulkersonFord-Fulkerson 算法添加了回流这一步操作,假如选到了阻塞流,则可以进行回流修正,从而解决了问题。

图示

​ 我们以刚刚的基本算法的示例图来进行算法介绍

  • 依旧进行初始化
image-20240527202545598
  • 第一轮循环

    • 这三步和之前的 naive algorithmnaive\ algorithm​ 都是一样的,关键在于之后的一步
    image-20240527202806129 image-20240527202848483 image-20240527203213518
    - 在删除了饱和边之后,$Ford-Fulkerson\ Algorithm$ 增加了一步回流法:画出该增广路径的回流边,即从 $t$ 到 $s$​ 的流向。并且这些回流边在下一轮选择路径试仍然可以被选中。
    - 为什么可以这么做?这样做对算法的正确性有无影响?这里先介绍完算法,后文会给出正确性证明。
    
image-20240527203601427
  • 第二轮循环

    • 依旧如此,进行同样的操作
    image-20240527204940676 image-20240527205000332 image-20240527205024745
    - 此时我们注意到:在 $v_1 \to s$​ 这条路径,有两条回流边,此时我们很容易就会想到一个问题,这两条边,是否可以合并? - 答案是肯定的,同样的,我们在算法正确性小节会给出证明 - 第二轮循环结束的图如下: image-20240527205616413
  • 第三轮循环

    • 第三轮循环即是刚刚简单算法失败的地方,我们来看 FordFulkersonFord-Fulkerson​ 算法是如何纠正的
      • 此图很清晰地说明了回流边的作用——给后面的纠错预留操作空间
image-20240527210215619 image-20240527210601165 image-20240527210643283 image-20240527210725603
  • 循环结束

    • 此时,图中没有从 sstt​ 的路径了,于是算法终止。
    image-20240527211359230
    • 因此,我们可以得到流量图,且该图的 Maximum FlowMaximum\ Flow55
    image-20240527211456351 image-20240527211526813

算法实现步骤

刚刚我们介绍了 Naive AlgorithmNaive\ Algorithm ,对于 FordFulkersonFord-Fulkerson 算法的步骤,也只需添加一句话:

  • 创建一个残存图 (residual graph)(residual\ graph) ,初始化残存容量
  • whilewhile 可以找到增广路径 :
    • 找一条增广路径
    • 找出增广路径中的瓶颈容量 xx
    • 更新这条增广路径上每一条边,cf=cfxc_f=c_f-x​​ ,并删除饱和边
    • ==添加该增广路径的回流边==

最坏时间复杂度分析

  • whilewhile​​ 循环最坏情况

    ​ 不难看出,该图的最大流为 200200 ,若选择路径时,非常"好运"地选择了 sv1v2ts \to v_1 \to v_2 \to t 这条路径,则会发生一些 amazingamazing 的事情,即后面两张图。此时,循环就会进行 200200 遍,即进行 Amount of MaxFlowAmount\ of\ MaxFlow 遍。

image-20240527214001672 image-20240527215039323 image-20240527215103438

​ 以下给出证明:

​ 由于每一轮循环中,流量值至少增加 11FlowvalueFlowvalue00 开始增长到 MaxFlowMaxFlow

​ 所以,IterationsAmount of MaxFlowIterations \le Amount\ of\ MaxFlow

​ 在这个例子中,Iterations=Amount of MaxFlowIterations = Amount\ of\ MaxFlow

​ 综上所述,循环的次数为:O(f)O(f^*) ,其中 ff^* 代表最大流的大小。

  • 寻找路径最坏情况

    ​ 假设有向图 GG 中有 EE 条边,VV 个结点,并且所有的结点都至少有一条相邻边,则 EV2E \ge \frac{V}{2}

    ​ 所以如果使用 bfsbfsdfsdfs ,在一个残存网络中找到一条路径的时间可以化为:O(V+E)=O(E)O(V+E')=O(E)

  • 最坏时间复杂度

    ​ 综上所述,最坏时间复杂度为:O(Ef)O(Ef^*)

*算法正确性证明

​ 在算法介绍中,我们得知了设置回流边的意义,现在,我们来探究为什么能设置回流边,以及我们对回流边所进行的操作,是否会对算法正确性有所影响。

对于回流操作正确性的证明

​ 我们知道,残存网络(residual graphresidual\ graph)是一个特殊的流网络,其允许反向边,也即平行边的存在。

​ 那么对于残存网络中,这"特殊"的流 ff' ,我们定义 $f \uparrow f’ $ 为流 ff'ff 的递增操作,定义为:

(ff)(u,v)={f(u,v)+f(u,v)f(v,u)(u,v)E0其他(f\uparrow f')(u,v)=\begin{cases}f(u,v)+f'(u,v)-f'(v,u) &若(u,v)\in E \\0 &其他 \end{cases}

​ 在残存网络中将流量发送到反向边上等同于在原来的网络中缩减流量,所以将边 (u,v)(u,v) 的流量增加 f(u,v)f'(u,v) ,但减少了 f(v,u)f'(v,u) 。在残存网络中,这种将流量推流回去也称为抵消操作cancellationcancellation​)。

​ 对于上述操作,其满足容量限制性质以及流量守恒性质,证明如下:

​ 先证明容量守恒性质,

​ 设边 (u,v)E(u,v)\in E ,则 cf(v,u)=f(u,v)c_f(v,u)=f(u,v) ,且 f(v,u)cf(v,u)=f(u,v)f'(v,u) \le c_f(v,u)=f(u,v) ,因此有,

(ff)(u,v)=f(u,v)+f(u,v)f(v,u)f(u,v)+f(u,v)f(u,v)=f(u,v)0(ff)(u,v)=f(u,v)+f(u,v)f(v,u)f(u,v)+cf(u,v)=f(u,v)+c(u,v)f(u,v)=c(u,v)\begin{align*} (f \uparrow f')(u,v) &=f(u,v)+f'(u,v)-f'(v,u)\\ &\ge f(u,v)+f'(u,v)-f(u,v)\\ &=f'(u,v)\\ &\ge 0\\ \\ (f \uparrow f')(u,v) &=f(u,v)+f'(u,v)-f'(v,u)\\ &\le f(u,v)+c_f(u,v)\\ &=f(u,v)+c(u,v)-f(u,v)\\ &=c(u,v) \end{align*}

​ 所以,0(ff)(u,v)c(u,v)0 \le (f \uparrow f')(u,v) \le c(u,v) ,即该操作满足容量限制性质。

​ 再证明流量守恒性质,

​ 因为 ffff' 都遵循流量守恒性质,所以对于所有的节点 uV{s,t}u \in V-\{s,t\} ,有,

vV(ff)(u,v)=vV(f(u,v)+f(u,v)f(v,u))=vVf(u,v)+vVf(u,v)vVf(v,u)=vVf(v,u)+vVf(v,u)vVf(u,v)=vV(f(v,u)+f(v,u)f(u,v))=vV(ff)(v,u)\begin{align*} \sum_{v \in V}(f \uparrow f')(u,v) &=\sum_{v \in V}(f(u,v)+f'(u,v)-f'(v,u))\\ &=\sum_{v \in V}f(u,v)+\sum_{v \in V}f'(u,v)-\sum_{v \in V}f'(v,u)\\ &=\sum_{v \in V}f(v,u)+\sum_{v \in V}f'(v,u)-\sum_{v \in V}f'(u,v)\\ &=\sum_{v \in V}(f(v,u)+f'(v,u)-f'(u,v))\\ &=\sum_{v \in V}(f \uparrow f')(v,u)\\ \end{align*}

​ 其中第二行推导到第三行使用了 ffff' 的流量守恒性质。

​ 所以,vV(ff)(u,v)=vV(ff)(v,u)\sum_{v \in V}(f \uparrow f')(u,v)=\sum_{v \in V}(f \uparrow f')(v,u)​ ,即该操作满足流量守恒性质。

​ 因此,对于 fff\uparrow f'​ 这个操作,其满足容量限制性质以及流量守恒性质。

​ 所以回流操作并不会影响到流的基本性质,所以这个操作是正确的。

对于函数 ff|f \uparrow f'| 值大小的证明

​ 那么如果根据流的值的定义,不妨猜想 ff=f+f|f \uparrow f'|=|f|+|f'|​ ,证明如下:

​ 根据定义,有:

ff=vV(ff)(s,v)vV(ff)(v,s)|f \uparrow f'|=\sum_{v\in V}(f \uparrow f')(s,v)-\sum_{v \in V}(f \uparrow f')(v,s)

​ 因为对于每个节点 vVv \in V ,可以有边 (s,v)(s,v)(v,s)(v,s) ,但是二者不允许同时存在。

​ 因此我们定义 V1={v:(s,v)E}V_1=\{v:(s,v) \in E\} 为有边从源节点到达的节点集合,V2={v:(v,s)E}V_2=\{v:(v,s) \in E\} 为有边通往源节点的节点集合。我们有 V1V2VV_1 \cup V_2 \subseteq VV1V2=V_1 \cap V_2 = \emptyset

​ 所以上式可化为:

ff=vV(ff)(s,v)vV(ff)(v,s)=vV1(ff)(s,v)vV2(ff)(v,s)=vV1(f(s,v)+f(s,v)f(v,s))vV2(f(v,s)+f(v,s)f(s,v))=vV1f(s,v)+vV1f(s,v)vV1f(v,s)vV2f(v,s)vV2f(v,s)+vV2f(s,v)=vV1f(s,v)vV2f(v,s)+vV1V2f(s,v)vV1V2f(v,s)\begin{align*} |f \uparrow f'| &=\sum_{v\in V}(f \uparrow f')(s,v)-\sum_{v \in V}(f \uparrow f')(v,s)\\ &=\sum_{v\in V_1}(f \uparrow f')(s,v)-\sum_{v \in V_2}(f \uparrow f')(v,s)\\ &=\sum_{v\in V_1}(f(s,v)+f'(s,v)-f'(v,s))-\sum_{v\in V_2}(f(v,s)+f'(v,s)-f'(s,v))\\ &=\sum_{v\in V_1}f(s,v)+\sum_{v\in V_1}f'(s,v)-\sum_{v\in V_1}f'(v,s)-\sum_{v\in V_2}f(v,s)-\sum_{v\in V_2}f'(v,s)+\sum_{v\in V_2}f'(s,v)\\ &=\sum_{v\in V_1}f(s,v)-\sum_{v\in V_2}f(v,s)+\sum_{v \in V_1 \cup V_2}f'(s,v)-\sum_{v \in V_1 \cup V_2}f'(v,s)\\ \end{align*}

​ 又因为当 vV1v \in V_1 时,对于在集合 V2V_2 的边,vv 对应的流 ff00 ,即每一个额外的项都为 00 ,反之亦然。

​ 所以可以将 V1,V2,V1V2V_1,V_2,V_1\cup V_2 扩展到整个节点范围 VV 中。

​ 因此:

ff=vV1f(s,v)vV2f(v,s)+vV1V2f(s,v)vV1V2f(v,s)=vVf(s,v)vVf(v,s)+vVf(s,v)vVf(v,s)=f+f\begin{align*} |f \uparrow f'| &=\sum_{v\in V_1}f(s,v)-\sum_{v\in V_2}f(v,s)+\sum_{v \in V_1 \cup V_2}f'(s,v)-\sum_{v \in V_1 \cup V_2}f'(v,s)\\ &=\sum_{v\in V}f(s,v)-\sum_{v\in V}f(v,s)+\sum_{v \in V}f'(s,v)-\sum_{v \in V}f'(v,s)\\ &=|f|+|f'|\\ \end{align*}

​ 即:ff=f+f|f \uparrow f'|=|f|+|f'|

对于合并操作正确性的证明

​ 我们设 G=(V,E)G=(V,E) 为一个流网络,设 ffGG 中的一个流,设 pp 为残存网络 GfG_f 中的一条增广路径。假定将 ff 增加 fpf_p 的量,则函数 ffpf \uparrow f_p 是图 GG 中的一个流,由上述证明可知其值为 ffp=f+fp|f \uparrow f_p|=|f|+|f_p|

​ 又因为合并的操作视为 fp1fp2|f_{p_1} \uparrow f_{p_2}| ,所以合并后的值为 fp1fp2=fp1+fp2|f_{p_1} \uparrow f_{p_2}|=|f_{p_1}|+|f_{p_2}| ,即合并操作是合理的

最大流最小切割定理的证明

​ 上述证明解释了为什么我们可以进行回流、合并(MergeMerge) 这些操作。那么接下来,我们来证明为什么最后得到的流一定是最大流。该证明也是对最大流最小切割定理的证明。

前置证明准备

​ 我们首先证明对于网络中的一个流以及任意切割 (S,T)(\mathcal{S},\mathcal{T}) ,其净流量 f(S,T)=ff(\mathcal{S},\mathcal{T})=|f|

​ 对于任意节点 uV{s,t}u \in V-\{s,t\} ,其流量守恒定律可改写成:

vVf(v,u)vVf(u,v)=0\sum_{v \in V}f(v,u)-\sum_{v \in V} f(u,v)=0 \quad\quad①

​ 根据 f|f| 的定义:f=vVf(s,v)vVf(v,s)|f|=\sum_{v\in V}f(s,v)-\sum_{v \in V}f(v,s) ,我们得到:

f=vVf(s,v)vVf(v,s)|f|=\sum_{v\in V}f(s,v)-\sum_{v \in V}f(v,s) \quad\quad②

​ 将 式针对所有节点 S{s}\mathcal{S}-\{s\} 求和并加到 中,得到:

f=vVf(s,v)vVf(v,s)+vS{s}(vVf(u,v)vVf(v,u))=vVf(s,v)vVf(v,s)+vS{s}vVf(u,v)vS{s}vVf(v,u)=vV(f(s,v)+vS{s}f(u,v))vV(f(v,s)+vS{s}f(v,u))=vVuSf(u,v)vVuSf(v,u)\begin{align*} |f| &=\sum_{v\in V}f(s,v)-\sum_{v \in V}f(v,s)+\sum_{v \in \mathcal{S}-\{s\}}(\sum_{v\in V}f(u,v)-\sum_{v \in V}f(v,u))\\ &=\sum_{v\in V}f(s,v)-\sum_{v \in V}f(v,s)+\sum_{v \in \mathcal{S}-\{s\}}\sum_{v\in V}f(u,v)-\sum_{v \in \mathcal{S}-\{s\}}\sum_{v \in V}f(v,u)\\ &=\sum_{v\in V}(f(s,v)+\sum_{v \in \mathcal{S}-\{s\}}f(u,v))-\sum_{v \in V}(f(v,s)+\sum_{v \in \mathcal{S}-\{s\}}f(v,u))\\ &=\sum_{v\in V}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in V}\sum_{u \in \mathcal{S}}f(v,u) \end{align*}

​ 又因为 V=STV=\mathcal{S}\cup\mathcal{T} 并且 ST=\mathcal{S} \cap \mathcal{T}=\emptyset ,所以可以将上式的 VV 进行分解,即:

f=vVuSf(u,v)vVuSf(v,u)=vSuSf(u,v)vSuSf(v,u)+vTuSf(u,v)vTuSf(v,u)=vTuSf(u,v)vTuSf(v,u)+(vSuSf(u,v)vSuSf(v,u))\begin{align*} |f| &=\sum_{v\in V}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in V}\sum_{u \in \mathcal{S}}f(v,u)\\ &=\sum_{v\in \mathcal{S}}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in \mathcal{S}}\sum_{u \in \mathcal{S}}f(v,u)+\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(v,u)\\ &=\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(v,u)+(\sum_{v\in \mathcal{S}}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in \mathcal{S}}\sum_{u \in \mathcal{S}}f(v,u))\\ \end{align*}

​ 不难看出,上式括号内的两个求和项是一样的,所以有:

f=vTuSf(u,v)vTuSf(v,u)=f(S,T)|f|=\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(v,u)=f(\mathcal{S},\mathcal{T})

​ 综上,净流量 f(S,T)=ff(\mathcal{S},\mathcal{T})=|f|​ 。

​ 由这个性质,我们可以得到一个引理:fc(S,T)|f| \le c(\mathcal{S},\mathcal{T}) ,证明如下:

f=f(S,T)=vTuSf(u,v)vTuSf(v,u)vTuSf(u,v)vTuSc(u,v)=c(S,T)\begin{align*} |f| &=f(\mathcal{S},\mathcal{T})=\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(v,u)\\ &\le \sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(u,v) \le \sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}c(u,v) = c(\mathcal{S},\mathcal{T}) \end{align*}

​ 这个引理给出的一个直接结论就是:一个流网络中最大流的值不能超过该网络最小切割的容量

最大流最小切割定理

​ 那么接下来,我们就来证明最大流最小切割原理。该原理表明一个最大流的值等于一个最小切割的容量。

最大流最小切割定理

(1)(1) ffGG 的一个最大流

(2)(2) 残存网络 GfG_f 不包括任何增广路径

(3)(3) f=c(S,T)|f|=c(\mathcal{S},\mathcal{T}) ,其中 (S,T)(\mathcal{S},\mathcal{T}) 是流网络 GG 的某个切割。即 fmax=cmin(S,T)|f|_{max}=c_{min}(\mathcal{S},\mathcal{T})

​ 上述的三个条件是等效的,即三个条件互为充要条件。

​ 以下是最大流最小切割定理的证明:

​ 证 (1)(2)(1) \to (2)

​ 假设 ffGG 的一个最大流,但残存网络 GfG_f 同时存在一条增广路径 pp 。如果我们对 ff 增加流量 fpf_p ,那么 ffp=f+fp>f|f \uparrow f_p|=|f|+|f_p| > |f| ,与 ff 是最大流矛盾。

​ 证 (2)(3)(2) \to (3)

​ 对于节点 uSu \in \mathcal{S}vTv \in \mathcal{T} ,如果边 (u,v)E(u,v) \in E ,则必有 f(u,v)=c(u,v)f(u,v)=c(u,v) ,否则 (u,v)Ef(u,v) \in E_f ,上述操作会将 vv 置于集合 S\mathcal{S} 中。如果边 (v,u)E(v,u) \in E ,则必有 f(v,u)=0f(v,u)=0 ,否则 cf(u,v)=f(v,u)c_f(u,v)=f(v,u) 将为正值,边 (u,v)(u,v) 将属于 EfE_f ,上述操作会将节点 vv 置于集合 S\mathcal{S} 中。当边 (u,v)(u,v)(v,u)(v,u) 都不在 EE 中,则 f(u,v)=f(v,u)=0f(u,v)=f(v,u)=0 。因此:

f(S,T)=vTuSf(u,v)vTuSf(v,u)=vTuSc(u,v)vTuS0=c(S,T)f(\mathcal{S},\mathcal{T})=\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(u,v)-\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}f(v,u)=\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}c(u,v)-\sum_{v\in \mathcal{T}}\sum_{u \in \mathcal{S}}0=c(\mathcal{S},\mathcal{T})

​ 证 (3)(1)(3) \to (1)

​ 由 fc(S,T)|f| \le c(\mathcal{S},\mathcal{T}) 即可说明 ff​ 是一个最大流,。

​ 综上所述,FordFulkersonFord-Fulkerson 算法是正确的。

EdmondsKarpEdmonds-Karp​ 算法

​ 简单来说,EdmondsKarpEdmonds-Karp 算法是 FordFulkersonFord-Fulkerson 算法的一种特殊情况,全部流程跟 FordFulkersonFord-Fulkerson 算法基本一致。

​ 唯一的区别是 EdmondsKarpEdmonds-Karp 算法在寻找增广路径中,使用寻找无权图最短路径的算法,在残存网络中将所有边的权值视为 11 ,由此来寻找从源节点到汇点的最短路径。

算法实现步骤

参照 FordFulkersonFord-Fulkerson 算法的步骤,对于 EdmondsKarpEdmonds-Karp 算法的实现步骤只需修改一句话:

  • 创建一个残存图 (residual graph)(residual\ graph) ,初始化残存容量
  • whilewhile 可以找到增广路径 :
    • ==找到最短的增广路径==
    • 找出增广路径中的瓶颈容量 xx
    • 更新这条增广路径上每一条边,cf=cfxc_f=c_f-x​​ ,并删除饱和边
    • 添加该增广路径的回流边

时间复杂度分析

对于寻找最短的增广路径,我们可以使用类 BFSBFS 的算法。

因此寻找最短路径的时间复杂度为:O(V+E)=O(E)O(V+E)=O(E)

然后循环最多实现 O(VE)O(VE) 次~~(这里就不证明了吧 详细证明看算法导论)~~

因此时间复杂度为:O(VE2)O(VE^2)

*DinicDinic 算法

​ 下面我们来介绍一个时间复杂度更低,达到了 O(V2E)O(V^2E) 的算法—— DinicDinic 算法。

Level GraphLevel\ Graph

DinicDinic 算法需要用到一个新东西:Level GraphLevel\ GraphLevel GraphLevel\ Graph 基于 BFSBFS 实现,是一个有权无向图。其随着层数记录每一层的节点,最终到达汇点 tt

  • e.g.
image-20240530002554265 image-20240530002640623

算法实现步骤

该算法的实现与 FordFulkersonFord-Fulkerson 算法类似,但在操作中,我们主要以 Level GraphLevel\ Graph 为主要操作对象:

  • 创建一个残存图 (residual graph)(residual\ graph)​ ,初始化残存容量
  • whilewhile 可以找到阻塞流:
    • 用残存图创建 level graphlevel\ graph
    • level graphlevel\ graph 中找到阻塞流
    • 更新残存图,并添加反向边

图示

  • 初始化
image-20240530004250278
  • 第一次循环

    • 根据残存图创建 level graphlevel\ graph
    image-20240530004356587 image-20240530004424301
    • 找到阻塞流,更新残存图
    image-20240530005049595 image-20240530005117998 image-20240530005131807
  • 第二次循环

    • 根据上一次的结果,创建新的 level graphlevel\ graph
    image-20240530005340427 image-20240530005357720
    • level graphlevel\ graph​ 中找到阻塞流,更新残存图
    image-20240530005947737 image-20240530010051945 image-20240530010116795
  • 第三轮循环

    • 根据上一次的结果,创建新的 level graphlevel\ graph
    image-20240530010116795 image-20240530010243668
    • 因为没有边能到汇点 tt ,循环终止,最终结果如下:
    image-20240530010437401 image-20240530010504841

时间复杂度

时间复杂度为:O(V2E)O(V^2E)

  • 若图为一个链表时,有 nn 层,循环最多为 O(V1)O(V-1)
  • 每一轮的时间为 O(VE)O(VE)

二部图问题

嘛~有时间在做吧awa 反正是最大流之后的一章(4th edition)

参考文献

  1. Wang, Shusen. (n.d.). AdvancedAlgorithms. GitHub. Retrieved April 5, 2022, from https://github.com/wangshusen/AdvancedAlgorithms
  2. Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2009). 算法导论 (第三版). 机械工业出版社.
  3. Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2022). Introduction to Algorithms (4th ed.). MIT Press.