最大流
网络流问题
给定指定的一个有向图,其中有两个特殊的点源S(Sources)和汇T(Sinks),每条边有指定的容量(Capacity),求满足条件的从S到T的最大流(MaxFlow)。
显然有流网络有三个性质:1.容量限制:流量不会大于容量。2.反对称性:从u到v的流量一定是从v到u流量的相反值。3.流守恒性:流入与流出相同。这里要特意注意第二条,一条边上的流量v会带来反向的流量c-v,具体体现在代码,就是在残留网络中每条边都有反边,当一条边的流量增加,它的反边的流量就减少相应值。网络流的算法就是基于残留网络而不是基于原网络的。
残留网络,增广路和割
增广路就是一条通过残留网络的流,其作用是“反悔”。割C[A, B]是将图G分为A和B两个点集,使得S在A中,T在B中。注意所谓“割中的边”,指的是从属于S的点出发,到T的所有边。
如何求出一个网络的最大流呢?有一个最简单的EK算法,思路是每次找出一条增广路,不断调整残留网络,直到没有增广路为止。这个证明可以感性愉悦一下。下面是理性不愉悦的 。
对于一个网络流图G=(V,E),其中有源点s和汇点t,那么下面三个条件是等价的:
- 流f是图G的最大流
- 残留网络Gf不存在增广路
- 对于G的某一个割(S,T),此时f = C(S,T)
首先证明1 => 2:
我们利用反证法,假设流f是图G的最大流,但是残留网络中还存在有增广路p,其流量为fp。则我们有流f'=f+fp>f。这与f是最大流产生矛盾。
接着证明2 => 3:
假设残留网络Gf不存在增广路,所以在残留网络Gf中不存在路径从s到达t。我们定义S集合为:当前残留网络中s能够到达的点。同时定义T=V-S。
此时(S,T)构成一个割(S,T)。且对于任意的u∈S,v∈T,有f(u,v)=c(u,v)。若f(u,v)<c(u,v),则有Gf(u,v)>0,s可以到达v,与v属于T矛盾。
因此有f(S,T)=Σf(u,v)=Σc(u,v)=C(S,T)。
最后证明3 => 1:
由于f的上界为最小割,当f到达割的容量时,显然就已经到达最大值,因此f为最大流。
这样就说明了为什么找不到增广路时,所求得的一定是最大流,大概思想就是利用割作为跳板来证明。
Dinic
我们来算一下EK的复杂度。首先我们定义层次图。将残留网络从s开始bfs,计算出每个点是第几层的,层次图中只包含从层次低连到高的边。将t的层数定义为层次图的层数。相同层数的层次图称为一个阶段。
设原残留网络的层数为k。容易发现增广一次,k要么不变,要么增加。k增加的原因是增广是在层次图上增广的,不会使层次图中的边流量增加,只会增加后向边的流量,因此bfs会走更多的回头路,层数随之增加。(此处感性愉悦,具体见)
因此随着增广,层次图的层数严格递增。由于最短路的长度最少为1,最多为n-1,所以最多有n-1个阶段,近似于n。在同一阶段中,每增广一次,就会至少减少一条层次图中的边。所以最多增广m次。而EK算法找增广路用的bfs,所以时间复杂度为O(n*m^2)。
Dinic唯一做的就是把bfs改成dfs。在每一个阶段中,先用一个bfs将所有的点的层次都确定出来,然后强制dfs只能从层次浅的跑向深的点。这样就可以用dfs代替bfs了。时间复杂度被优化到O(n^2*m),考场上一般写这个。
注意我加了一个当前弧优化,就是dfs时不用跑已经跑过的弧了,因为边的流量只会少不会多,并且层次图是个dag。
#include#include #include using namespace std;const int maxf=55, maxn=maxf*200, maxm=maxn+100, INF=1e9, inf=1e8;int n, m, cnt, src, dst, ans;inline int min(int x, int y){ return x r[u]) continue; //beg[u]+j-l[u]表示x[u]=x[v]+d的点的编号 addedge(max(beg[u], beg[u]+j-l[u]), beg[v]+i-l[v], 2*inf); } } printf("%d\n", inf*n-Dinic()); return 0;}
至于sap。。反正我是不准备学了(不会用到.jpg)