网络流——最大流EK算法讲解

网络流——最大流EK算法讲解

好了,这是第二篇博客了,如第一篇所述,来讲一讲刚刚理解的网络流。因为本人只会EK算法,所以先讲这个算法。(我会去补知识点的!!!

什么是网络流???

读者们刚接触这个知识可能会有点,不,可能是非常虽然本蒟蒻也只是懵懵懂

如最短路,我想即使先不学,手算也能将一个简单的图求出来。

但是遇到网络流——最大流,那恐怕不是那么好算,反正一开始给我算我也只是一头雾水。(有可能是本人太了,各位大佬请谅解)

给个图(感谢洛谷的图)
在这里插入图片描述
首先我们要先知道最大流的意思,简单来说有一个源点s,一个汇点t(如图),水流通过其他点从源点s到汇点t,请问可经过的最多水流。

那如上图,怎么求最大流呢?先手算一遍。

首先我们先理解理解:

图中的边权值意味着水流的最大流量(也许作者用词不精,但不妨碍理解吧?),即水流最大就边权值那么大。

我们再想想,水流怎么到达汇点的呢?显然,上述提及,是依靠其他点。那图中,有许多路径可从s到t,如:
s—>3—>t
s—>1—>t
s—>5—>t
s—>1—>3—>t

注意:图中所有的边都为有向边。

好了,那么先想:每一条路径的最大流量是多少呢?
。。。。。。
应为路径上的最小边权值。

No!!!

以刚才那张图为例(再一遍,因为我知道再翻上去有多麻烦!)

我们用s,1,2,3,t这几个点来举栗子

若按最大流为所有路径之和的思想,单看这几个点,ans应为:

s—>3—>t
流量:min(10,15)=10

s—>1—>t
流量:min(22,68)=22

s—>1—>3—>t
流量:min(22,34,15)=15

s—>1—>2—>3—>t
流量:min(22,6,9,15)=6

ans=10+22+15+6=53

对吗!!
在这里插入图片描述
为什么错?
首先看到s,1;s,3 这两条边。可以看出边权为22,10。理解一下,(忽略点4,5)我就给了你这么多水,任你怎么去分配,仍不可能大于我原来给你的水。所以上述算的ans理论上就不是正确的。那么原因是什么?

以点1为例,s流入点1的水(22)会流向点3,点2,点t。那么思考,流向每一个点的水流都会是22吗?不会。22是分配给了这三个点,所以应该是22为流向3个点的总和。

但注意,路径途中看可能会有水流失,如s—>4的水流就流失了,5—>t流不到56。

那正解呢?单说这道题,通过我们人的智慧,脑中应可以想出个贪心(应该是吧?)如点1,点2汇到点3想一想完全不需要。虽然3—>t还会多出5的流量,可以选择再补一点,但是补的是哪里的呢?s—>1的22里面的。那我完全可以将这22全从1汇入t,一次性搞定那不香吗 。所以用不着将点1的水汇一些给点3。

再举个栗子:3—>5其实也不需要走,因为:

  1. s汇给3的流量10完全可以直接从3—>t汇入,而且可以汇完,与上所说一样,所以不用汇给5。
  2. s—>4就给了56的流量,这的流量从5—>t都汇不完,但我再加个(11)3—>5又有什么用呢?

所以大概的思路就有了,那么依靠我们自身的判断,上图最大流应为22+10+45=77。

好了,我们总算手玩一遍了,那这种思想可以与程序结合吗?答案是肯定的。但是做到这一点还有一个问题:

上面说的什么什么路径不用走,是靠我们人的判断想出来的,但机器是死的,如何让它知道该走哪一条路呢?如果不注意这个问题,那结果即是得不到最优解。

当然,读者们也许还不知道如何先求最大流(也就是抛开上面问题的程序实现),但别着急,我们理解了这么多吗,还是先理解一些定义。(下面只是介绍,助于理解,看不懂没关系,但是有些必需看懂的等会会提醒。)

网络流、最大流定义

这就是硬东西了:
一、网络流的定义:有向图G=(V,E)中,点集中有一源点S,一汇点T。且S入度为0,T出度为0。对于每条边edge,都有一权值函数c,表示其容量,一权值函数f,表示其实际流量。

二、最大流的定义:在不违背网络流的定义下,S到T的最大流量。

好了,不管看不看的懂,没事,知道什么是网络流,什么是最大流就行了。(其实是自己也不太清楚,也说不清楚

那么刚刚我们就有了一个初步的思路,找到一条路径,ans+=min(边权)。对了,因为是手算,没说一个要点,(别怪我)

要点:想一想,但我们找到一条路径时,得到一个答案时,是不是路径上每一条边权都要减去最小边权呢。对吧?换到生活上,也是如此。因为一个边权,通过此边权有一条路径,那我分配了一些水过去,那自身流量是不是也要减少?因为我剩下的流量要给其他未被找到的路径用,不可能把分配过的水再次用,那这样就错了。

这是实现程序的一个步骤。

还有一个定义:

增广路:

增广路是指从s到t的一条路,流过这条路,使得当前的流(可以到达t的人)可以增加。

这个东西其实就是刚刚说的路径,但更加详细。

那么求最大流问题可以转换为不断求解增广路的问题,并且,显然当图中不存在增广路时就达到了最大流。

思路如下:

  1. 找到一条路径(即增广路),这里用bfs实现。
  2. 再得到路径的最小边权,累加答案,并且路径中的边权减去其。(这样也避免宽搜里死循环)
  3. 路径反向边加上最小边权
  4. 重复此过程,直到结束循环,此情况下也便找到了最大流。

那么程序就这么easy!!!
所以3是什么意思
好吧,其实这个步骤也就是来解决刚刚我们说过的问题:

梳理一下即是:在做增广路时可能会阻塞后面的增广路,或者说,做增广路本来是有个顺序才能找完最大流的。

为什么加反向边?

其实也就是给予程序一个反悔的机会。

因为我们是任意找的,为了修正,就每次将流量加在了反向弧上(其实就是反向边),让后面的流能够进行自我调整。

其实就是把一个路径的一条边退了回去,让它走另一条路径。

若你还是有些懵,没事,先看看代码。

所以说,这就是最大流的解法(其一),你如果理解了反向边的概念。那恭喜你,成功理解了

EdmondsKarp算法

这里先说一下复杂度为:
O(VE^2)

EK算法还是很容易理解的,并且代码实现很简单,当整个图是稀疏图的时候,使用EK算法不失为一种简便可行的方法,但是如果图的边数非常多,这个算法的性能也就显得不是那么优秀。可以看到EK算法的处理在每次找到增广路后,就从源点开始重新再BFS遍历查找,这一过程中有很多的遍历都是没必要的,虽然其他算法我也不会。

跟着思路放一下代码:

找到一条路径(即增广路),这里用bfs实现。

int bfs(){
   
	memset(b,1,sizeof(b));
	int np,flow=inf;//np为队首,flow为最小边权
	queue<int>q;//队列
	q.push(1);//1开始bfs
	b[1]=0;//标记
	pre[1]=pre[n]=-1;//pre[i]表示i点的上一个点,也就是从pre[i]走向i
	while(!q.empty
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值