猴子课堂:最大流与最小割
Posted zhangjianjunab
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了猴子课堂:最大流与最小割相关的知识,希望对你有一定的参考价值。
注:本人只是一个萌新,有一些地方的用语会不太专业(大佬:是十分不专业),我不会用什么技术用语,这些文章对那些跟我一样的萌新会比较适用!
最大流:
最大流我讲的是我自己对dinic算法的一些思想,希望对你会有用!
我记网络流靠三个关键字:
1.找最短路径
将流量流向终点,且损害最少的边,这是找路径的一个关键,那么,便可以把分出最少的层面,以便后面的查找!
如图:
分层
分层可以用宽搜(宽搜可以保证层数最少),用h数组储存层,具体如下代码:
那么,分层后有什么用呢?
答案就是!每个点只能流向比他大一层的点!这样既确定的边的最少损害,又确定的一些固定路径!
那么,到找流量了,一开始,从初始点出发有无限流量,搜索每条边,如图(滑稽为所在点,红字体为流量!)
搜索第二层的最上面那个点,刚好层数只比自己大一,边的剩余流量为16,自身还剩下999999999的流量可以用,于是,递归到第二层的最上面那个点,并赋予他流量16!
进入第二层的最上面那个点,找到了第二层最下面那个点,同层,无法进入,搜索第三层两个点,进入上面的点,将身上唯独的16赋予它,让他搜。
进入第二层的最上面那个点,找到了第二层最下面那个点,同层,无法进入,搜索第三层两个点,进入上面的点,将身上唯独的16赋予它,让他搜。
进入第三层上面那个点,一直搜索到第四层的终点,赋予流量5,进入终点。
搜索终点,发现为终点,返回自己身上现有流量。
回到第三层上面那个点,将总点返回的流量记录为t,表示从这条边走绝对可以到达的流量,并将这条边的值减t,将反向边的权值加t,再将t加到s(s代表已用流量),删除t,由此第三个点的现有可用流量从16变成11,继续搜索,搜索完了之后,将s返回,若s为0,顺便将这个点的层数标为0,代表在这种分层图中,这个点不能发挥他的神威,便在这个分层图内不在搜索他了(为什么说本次呢?因为要分多次并搜索多次才可以将所有的边全部巧妙的利用上)。
反向边(悔棋)处理讲在下次(十分重要)!
先给出搜索操作!
找答案!
进入第三层上面那个点,一直搜索到第四层的终点,赋予流量5,进入终点。
还剩下一个反向边,其实,一开始,所有边都要多建一条双向边(至少我是这样),反向边的流量为0!
反向边是网络流里面一个十分重要的思想
还记得这段代码吗?
反向边加t,简单来说就是给予反悔的机会!
例如:
为了解决这个小人的苦恼,我们建立一条反向边。小人就可以从反向边走到原本那个笨笨的人应该走的位置,并进行搜索,与其说是小人跑到那里,不如说是他把笨笨的人拉回来,让他将一些流量取回去,到那个点再次进行搜索,不过,不能走与以前一样的路径(否则叫他回来有什么用,不悔改有什么用?)。
虽然有时叫他回来可能他也没法再流到其他路径,这是,他会返回0,如果这样,和蔼的小人还是会理解他的,顶多不走就行了,因此反向边不会出现错误。
本蒟蒻的建边:
但是,Dinic不是最快的,比如:ISAP,预流推进算法等,也都是十分快的。(ISAP的博客我已经在我的博客置顶了)
不过Dinic比较简单,也十分简洁,在时间短的情况下我也更愿意敲Dinic。
好了,接下来处理最小割!
我并没有用算式证明,而是用了一种神奇的方法证明(科学家看了想骂人!):
首先,网络流的核心是从起点通过多条路径到终点,那么,我们可以想出,这每一条合格(可以从起点流向终点)的路径,一定会有其中一条或多条边爆流了,那么,代表这条边是这条路径的核心,取掉这条边,这条边的完整流量就会减去他的流量,我们称这些边为贡献边。
那么,在最小割所求的最小容量中所割掉的边的集合中,一定全部是贡献边;想不明白的同学可以躲到门后想几分钟,记得带上草稿纸和笔!(大佬:这不是我幼儿园就会的吗?)(我:滚!)
为什么呢(同学:我想好一会才想明白你告诉我有解释!)?由于这些贡献边是这些路径的重要部分,因此,取掉这些边,也就会使起点无法流向终点,也就无法到达终点,且由于这些边都是满的,不会有浪费!
注意:有时候,一条路径有多条贡献边,甚至会出现两条路径共用一条贡献边,这时会有多种组合(所以,你不能说一条路径上的两条贡献边都属于这个集合,只能用另外的方法判断!)!
由于,我们知道,这个最小割集合里的边,容量代表了整个图的流量,且他们都是爆流的!所以从起点到终点的所有流量绝对会流到他们,而他们自己的流量也绝对可以流向终点(具体看前面,一条边只会减去经过他的有效流量!),所以,最小割与最大流是一样的((想打人的)大佬:说话怎么这么不专业!)!
那么,怎么求这些边呢(编号按字典序排并且要最少的边)
由于这些贡献边控制了流量,所以,我们把他删除掉,再流一遍,会得到一个新的ans(ans可能是0),ans+这条边的权值=不删这条边的最大流,依靠这条等式,就可以去判断了,别忘了排序哟!
(注,其实如果只求割最少的边,还有另外一种方法,就是把每个容量乘以一个很大的数加1,流量=假流量/很大的数,割的边数=假流量%很大的数)
代码:
另外,有的最小割不是单纯的割边,而是割点,这时,可以讲一个点拆成两个点,两个点之间存在一条容量为1的单向边,进入的边连向一个点,出去的边连另一个点,便把割点化成割边(起点与终点不用分)!
如图(讲i拆开了!):
然后,就尽情最小割吧!
喜欢点个赞呗
注:上面的图片侵权抱歉!
以上是关于猴子课堂:最大流与最小割的主要内容,如果未能解决你的问题,请参考以下文章