运筹学—最大流模型
上两篇文章,我介绍了最短路径中的两种算法:
最短路径算法——清晰简单的弗洛伊德算法(Floyd)
最短路径算法——简单明了的迪杰斯特拉算法(Dijkstra)
这篇文章,我来简单介绍一下最大流模型!
最大流模型
\qquad很多的数学模型往往来源于生活问题,本文介绍其中一个问题借此引出最大流模型,让读者能够更好地了解模型的背景以及应用。
\qquad现有一管道网络用于运输原油,将原油从油井运输到提炼厂,为了在网络中顺畅地输运原油,需要在中间合适的距离安装增压泵站,每一段管道有一个有限的最大原油流量(容量),每段管道可以是单向,也可以是双向,取决于它的原始设计。那么如何确定从油井到提炼厂原油的最大流量?
\qquad下面给定双向弧的相关概念:
\qquad给定弧(i,j)(i,j)(i,j),其中i<ji<ji<j,用符号(C‾ij,C‾ji)(\overline{C}_{ij},\overline{C}_{ji})(Cij,Cji)表示两个方向i→j,j→ii→j,j→ii→j,j→i弧上的容量。为了更清晰地描述,把C‾ij\overline{C}_{ij}Cij放在靠近节点iii的一边,把C‾ji\overline{C}_{ji}Cji放在靠近节点jjj的一边,如下图所示。
C‾ij表示从i→j的弧上的容量,C‾ji表示从j→i的弧上的容量\overline{C}_{ij}表示从i→j的弧上的容量,\overline{C}_{ji}表示从j→i的弧上的容量 Cij表示从i→j的弧上的容量,Cji表示从j→i的弧上的容量
1 枚举割
\qquad割(cut) 是一部分弧的集合,如果将这些弧从网络中删除,就会断开源点和汇点之间所有的流。割的容量等于这个割中所有弧上容量的和。在网络中所有可能的割中,最小容量的割就对应了这个网络的最大流。
例子
如下图网络,按照上述表示方法对每一条双向弧都标上了容量。例如,对于弧(3,4),从节点3到节点4的流量限制为10单位,从节点4到节点3为5单位。我直接在图中,标出了该网络的3个割:
图中3个割的容量如下表:
| 1 | (1,2),(1,3),(1,4) | 20+30+10=60 |
| 2 | (1,3),(1,4),(2,3),(2,5) | 30+10+40+30=110 |
| 3 | (2,3),(3,5),(4,5) | 30+20+20=70 |
从表中可以看出,该网络最大流不超过60个单位。枚举所有割,对于小型网络是可行的,但是对于比较复杂的网络,效率就很低了,所以需要寻找其他算法。
2 最大流算法
2.1 原理
\qquad最大流算法的核心思想是:从网络中寻找从源点到汇点具有正的流的突破路径。
\qquad弧(i,j)(i,j)(i,j)上的初始容量(C‾ij,C‾ji)(\overline{C}_{ij},\overline{C}_{ji})(Cij,Cji),当给定一个流并找到突破路径后,需要对每条弧上的容量进行修改,修改后的容量称为剩余容量(cij,cji)(c_{ij}, c_{ji})(cij,cji)。 如果节点jjj从节点iii接收到一个流,那么给节点jjj标号为[aj,i][a_j,i][aj,i](节点jjj的表示)。其中aja_jaj表示从节点iii流到节点jjj的量,iii表示流的来源是节点iii。
2.2 步骤
第1步: 首先将所有弧(i,j)(i,j)(i,j)的剩余容量设为初始容量,即(cij,cji)=(C‾ij,C‾ji)(c_{ij}, c_{ji}) = (\overline{C}_{ij},\overline{C}_{ji})(cij,cji)=(Cij,Cji).取a1=∞a_1=\inftya1=∞,则源点1标号为[∞,−][\infty,-][∞,−]. 令i=1i=1i=1,进入第2步。
第2步: 确定集合SiS_iSi,它是节点iii通过剩余容量为正的弧能够直接到达的所有未标号的节点jjj的集合。如果Si=∅S_i=\emptysetSi=∅,进入第3步;否则,进入第4步。
第3步: 确定k∈Sik\in S_ik∈Si,使之满足cik=maxj∈Si{cij}c_{ik}= \mathop{max}\limits_{j\in S_i}\{c_{ij}\}cik=j∈Simax{cij}. 令ak=cika_k=c_{ik}ak=cik,并且给节点kkk标号[ak,i][a_k, i][ak,i].如果k=nk=nk=n,此时汇点(终点)得到了标号,也找到了一条突破路径,进入第5步;否则令i=ki=ki=k,转回第2步。
第4步(回溯): 如果i=1i=1i=1,那么不存在突破路径,进入第6步;否则,令rrr是紧邻在当前节点iii之前得到标号的节点,并将节点iii从节点rrr的相邻节点集合中删除,令i=ri=ri=r,转回第2步。
第5步(剩余容量的确定): 令Np=(1,k1,k2,...,n)N_p=(1,k_1, k_2,...,n)Np=(1,k1,k2,...,n)是从源点1到汇点nnn的第ppp条突破路径,则这条路径上最大流量为:
fp=min(a1,ak1,ak2,...,an)f_p=min(a_1,a_{k1},a_{k2},...,a_n) fp=min(a1,ak1,ak2,...,an)
\qquad这条路径上每条弧的剩余容量在顺着流的方向上fpf_pfp是减少的,在逆着流的方向上fpf_pfp是增加的。
\qquad例如,对于该路径上的节点iii与节点jjj,当前剩余容量为(cij,cji)(c_{ij}, c_{ji})(cij,cji)变为:
\qquad<\a> 如果流是从节点iii流向节点jjj,则弧上的剩余容量为(cij−fp,cji+fp)(c_{ij}-f_p, c_{ji}+f_p)(cij−fp,cji+fp);
\qquad<\b> 如果流是从节点jjj流向节点iii,则弧上的剩余容量为(cij+fp,cji−fp)(c_{ij}+f_p, c_{ji}-f_p)(cij+fp,cji−fp);
\qquad恢复第4步删除掉的节点,然后令i=1i=1i=1,转回第2步接着寻找新的突破路径。
第6步(最优解):
\qquad<\a> 给出已经找到的mmm条突破路径,那么网络的最大流为:
F=f1+f2+...+fmF=f_1 + f_2 + ...+ f_m F=f1+f2+...+fm
\qquad<\b> 根据每条弧(i,j)(i,j)(i,j)上的初始的和最终的剩余容量,(cij,cji)和(C‾ij,C‾ji)(c_{ij}, c_{ji})和 (\overline{C}_{ij},\overline{C}_{ji})(cij,cji)和(Cij,Cji)按照下面的方法确定该弧上的最优流:令(x,y)=(C‾ij−cij,C‾ji−cji)(x,y)=(\overline{C}_{ij}-c_{ij},\overline{C}_{ji}-c_{ji})(x,y)=(Cij−cij,Cji−cji),如果x>0x>0x>0,那么从节点iii流向节点jjj的最优流是xxx个单位;如果y>0y>0y>0,那么从节点jjj流向节点iii的最优流是yyy个单位.(注意x,yx,yx,y不能同时为正数).
3 总结
\qquad由于例子过于繁杂,这里就不做具体展示,有兴趣的读者可以参考运筹学相关书籍,这里推荐2本我觉得不错的,分别是:
《运筹学导论》 作者:[美]Hamdy A·Taha 出版社:人民邮电出版社;
《运筹学》 作者:运筹学教程编写组 出版社:清华大学出版社
| 运筹学导论 | 运筹学 |
总结
- 上一篇: 【Go】sync.RWMutex源码分析
- 下一篇: 最大流的四种常用算法