changxv大佬说网络流非常万能,几乎可以解决所有与图有关的题目(网络流的强悍!),这里写出博客只是谈谈我自己的心得体会,帮助那些看到网络流望而却步的同学们以及我自己有个大致了解。
概念:
在一张带权图G中,我们已知源点s与汇点t,假设我们要将水从s输送到t,其间必然会通过一些边以及其它中转点(边就像是水管,边权就是这个水管的最大容量即最大运送的水量),最大流问题就是要我们求在每个水管最大容量内最多能将多少水从s送到t。
变量定义:
对于每一条边,我们定义它的上限为容量cap(u,v),实际运送水量称为流量flow(u,v)
struct edge{
int from,to,cap,flow;//一般只用一个cap即可(看到后面就知道啦)
}
注意:从u->v的水管送2单位水,再从v->u的水管送4单位水没有意义,因为其等价于从v->u送(4-2=2)单位水。因此,对于每一条从u->v的边,我们建立一条正向边(正向弧)和一条反向边(反向弧),规定其中flow(u,v)>=0,flow(v,u)<=0。
最大流性质:
除了源点s与汇点t,其余结点都为中转点,所以有:
1、s的出水流量等于t的入水流量
2、flow(u,v)=-flow(v,u)
3、任意中转结点的总入水流量等于-总出水流量,即:正向弧flow+反向弧flow=0
增广路算法:
即是求解最大流的算法,算法思想很简单,即从零开始不断增加流量,并且每次都保持上述3条最大流性质。
如:对于一条边(u,v),若我们要向这条边送d单位的水量,则flow(u,v)+=d,flow(v,u)-=d。
我们每一次增广后的图称为残量网络,对于flow=cap的边,我们称它已满流,则在下一次增广中这条边是不能流动的(也可以说删除了这条边)。
显然,每次增广之后流量始终满足3条性质,所以只要残量网络中不存在增广路(s和t不连通),则当前流就是最大流。
由此可以看出我们每次都是找一条任意从s到t的可行路径,我们很容易想出dfs或bfs暴力算法,事实上我学的也就是dfs。
举个例子:假设有n个教室m个老师,第i个老师能在一些教室上课,现在我们让第一个老师选择他的教室,当第二个老师来的时候,他选择他的教室……直到一个老师a选定的一个教室已经被老师b占用,老师a会选择与老师b商量,那么老师b会寻找他的第二个教室,如果此时教室为空,则成功;如果此时