(通俗易懂小白入门)网络流最大流——EK算法
网络流
网络流是模仿水流解决生活中类似问题的一种方法策略,来看这么一个问题,有一个自来水厂S,它要向目标T提供水量,从S出发有不确定数量和方向的水管,它可能直接到达T或者经过更多的节点的中转,目前确定的是每条水管中水流的流向是确定的(单向),且每个水管单位时间内都有属于自己的水流量的上限(超过会爆水管),问题是求终点T单位时间内获得的最大水流量是多少?如下图:
1. 首先,我们用正常的思路去解决这个问题,对于上图的情况而言,我们可以先选择一条水流的路线1->2->4,而且我们得知1->2水管水流量上限为40,而2->4水管水流量上限只有20,则这一条路径通往终点T的水流最大值为20,而这么走的话,1->2的水管中剩余水流量则为40-20=20,2->4水管中的剩余水量为20-20=0,注意:这里水管剩余流量为0后说明这条路径就再也不能接通了,或者说它已经被完全占用了,此时终点T单位时间获得水量0+20=20
2. 然后我们继续选择一条不包含0流量的从S到T的路径,比如1->2->3->4,此时1->2上水流量剩余20,2->3剩余30,3->4剩余10,很显然这条路径上最终能为终点T提供的单位时间的水量由路径上的最小剩余流量10决定(就像是木桶的短板效应一样),这么走的话,1->2水管中剩余的水量为40-20-10=10,2-3水管中剩余的水量为30-10=20,3->4水管中剩余的水量为10-10=0,它也被完全占用了,此时终点T单位时间获得水量为20+10=30
3. 我们继续找还能找到一条从S流向T的路径1->4,水流上限20,因为是直达的,所以路径上最小剩余流量就是它本身,则这么走的话,1->4上的水流量全部耗尽,20-20=0,它也完全被占用了,而终点T再一次获得20水量,30+20=50,至此整张网络流图再也找不到一条能从S到T的不包含0流量的通路,终点T单位时间获得的最大水流也计算出来得到50
但是,上述的推理只是碰巧数字比较合适让我们轻而易举得到了50这个看似正确的结果,实际上存在着缺陷,假如我第一次找的一条路径并不是此时的最优解,并且这么选使得我下一次选择别的路径的时候有的水管流量已经被占完了,我想反悔怎么办
如下图:如果我们第一次选择的是1->2->3->4这条路径(这很符合程序设计的观念,计算机很容易就按照序号去查找),那么1->2水管剩余流量为1-1=0,2->3水管剩余流量为1-1=0,3->4水管的剩余流量为1-1=0,至此我们发现所有的通路都被这三个0流量的水管阻断了,而此时终点T获得的单位水量只有0+1=1
按正常的人为的思路,此时我想要反悔之前选的那条路径,同时选择别的路径,而下图的1->2->4和1->3->4这两条路径的选择才是这张网络流图的最优选择,终点T得到的最大水量为1+1=2,而2->3的这条水管我们放弃不用
上述反悔的过程在我们的思维看来确实可以(假装)没有走过2->3这条路,但是计算机并不能这么去主动理解这种情况,它应该按并不是最优的情况去尝试,当遇到可能更优的情况时更改之前的选择,在我们设计程序的时候,如何能做到这个反悔的过程呢,关键来了:我们对于每次选择的一条道路上的每个水管都要减去路径上的最小残量的同时,为这两个相邻的点添加一条反向弧,反向弧的数值加上最小残量的数值,如上上图那个并不是最优的走法,假设计算机确实第一次就选择了这条路径,那么我们做出如下处理:1->2剩余流量依旧为1-1=0,而同时添加一条2->1的反向弧,流量为0+1=1(反向弧也是一条通路,它的存在就像是为我们提供了一个返回的机会),2->3剩余流量为1-1=0,添加3->2的反向弧,剩余流量为0+1=1,同样3->4的剩余流量为1-1=0,而4->3的反向弧上的剩余流量为0+1=1,所以本次选择之后终点T依旧获得了1的单位流量
接下来我们继续选择一条路径,1->3->2->4(当然也只剩下这条了),此时我们如上述操作,为1->3剩余流量,3->2剩余流量,2->4剩余流量都减去这条路径上的最小残量1,同时为它们两两之间的反向弧增加上这个最小残量1(图可能有点乱,但是原理是不变的,实线方向-,虚线方向+),至此,1->2=0,2->1=1,1->3=0,3->1=1,2->3=1,3->2=0,2->4=0,4->2=1,3->4=0,4->3=1,从S到T已经没有一条额外的路径不包含0流量了,此时T获得最大单位水量2,并且通过这种建立反向弧的方式,我们对于从2到3再从3到2都走了一遍,这不就好像是模拟了一遍反悔的过程吗(具体证明这里不作详述),接下来讲解EK算法
铺垫了这么多终于要讲解EK算法了
别着急在讲解EK之前我们需要介绍几个网络流中最重要的概念,我们称起点S,也就是水流的出发点为源点,将水流的终点T称为汇点,而我们每一次找一条从源点到汇点的不包含0流量的路径称为增广路,(增广路顾名思义,如果能找到一条从S到T的增广路,则到终点的水流量一定还可以增加至少1,所以就满足了增广这个要求了),其实求网络流最大流的问题的各个算法都是在模拟一个找寻增广路的过程,如果找不到了就说明此时终点获得的水量将无法变得更大,答案就算出来了
EK算法:从S点出发不断找一条到T的增广路的过程,我们通过BFS向周围搜索与S直接相连的剩余流量不为0的节点(这个节点一定要是没走过的,因为一条增广路每个点肯定值出现了一次),将他们加入队列,每次从队列中取出一个元素继续向周围查询,直到目标点为T点,且这一条道路上不包含流量为0的水管,则说明这是一条增广路,为沿途的所有节点两两之间的剩余流量减去该条增广路的最小残量,而同时为它们的反向弧加上最小残量,不断循环直到无法从S点到T找到一条增广路为止
这里推荐一题网络流最大流的模板题,POJ1273,题面讲的是有n个水管,有m个点,源点为1,汇点为m,求汇点T单位时间的最大水流量,当然这题有个小坑,就是输入会重复,如果1->2 40代表从1流向2有40流量,那可能会有多次1->2 40,1->2 30之类的,要累加成1->2 70
代码:
#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std; const int N = ;
const int INF = 0x3f3f3f3f;
int c[N][N]; //记录i到j的剩余流量
int p[N]; //p[i]记录流向i点的前驱节点
int v[N]; //记录在一条增广路中,流到i点时,此刻增广路上残余量的最小值,直到i == m时就是整条增广路上的残余量最小值
int n, m; int min(int a, int b){
return a <= b ? a : b;
} void EK(){
//从1出发,不断找可以到达m的增广路
int ans = ;
while(true){
//EK算法的核心是通过bfs不断查找增广路,同时建立反向弧
//每次循环都要对v数组和p数组进行清空,因为是意图查找一条新的增广路了
memset(p, , sizeof(p));
memset(v, , sizeof(v));
queue<int> q;
q.push();
v[] = INF;
//每次只找一条增广路,同时修改c[i][j]的值
while(!q.empty()){
int f = q.front();
q.pop();
for(int i = ; i <= m; i++){
if(v[i] == && c[f][i] > ){ //v[i]原本是记录增广路实时的残量最小值,v[i]==0代表这个点还没有走过,且从p到i的残量大于0说明通路
v[i] = min(v[f], c[f][i]); //实时更新v[i]的值,v[f]存储1条增广路中i点前所有水管残量的最小值,v[i]为该条增广路到i点为止,路径上的最小残量
p[i] = f; //p[i]实时保存i点的前驱节点,这样就当i==m时整条增广路就被记录下来
q.push(i); //将i点入队
}
}
}
if(v[m] == ) break; //如果v[m]==0则代表找不到增广路了(中途出现了c[i][j]==0的情况)
ans += v[m];
int temp = m;
while(p[temp] != ){ //类似并查集的查操作,不断查上一个元素且将剩余残量减去最小残联,反向弧增加最小残量
c[p[temp]][temp] -= v[m];
c[temp][p[temp]] += v[m];
temp = p[temp];
}
}
printf("%d\n", ans);
} int main(){
while(scanf("%d%d", &n, &m) != EOF){
memset(c, , sizeof(c));
for(int i = ; i <= n; i++){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
c[x][y] += z; //初始时,从x流向y的剩余流量就是输入的值
}
EK();
}
return ;
}
(通俗易懂小白入门)网络流最大流——EK算法的更多相关文章
- 二分图的最大匹配——最大流EK算法
序: 既然是个图,并且求边数的最大值.那么这就可以转化为网络流的求最大流问题. 只需要将源点与其中一子集的所有节点相连,汇点与另一子集的所有节点相连,将所有弧的流量限制置为1,那么最大流 == 最大匹 ...
- 最大流EK算法/DINIC算法学习
之前一直觉得很难,没学过网络流,毕竟是基础知识现在重新来看. 定义一下网络流问题,就是在一幅有向图中,每条边有两个属性,一个是cap表示容量,一个是flow 表示流过的流量.我们要求解的问题就是从S点 ...
- 网络流最大流——dinic算法
前言 网络流问题是一个很深奥的问题,对应也有许多很优秀的算法.但是本文只会讲述dinic算法 最近写了好多网络流的题目,想想看还是写一篇来总结一下网络流和dinic算法以免以后自己忘了... 网络流问 ...
- 初探网络流:dinic/EK算法学习笔记
前记 这些是初一暑假的事: "都快初二了,连网络流都不会,你好菜啊!!!" from 某机房大佬 to 蒟蒻我. flag:--NOIP后要学网络流 咕咕咕------------ ...
- 最大流——EK算法
一.算法理论 [基本思想] 反复寻找源点s到汇点t之间的增广路径,若有,找出增广路径上每一段[容量-流量]的最小值delta,若无,则结束.在寻找增广路径时,可以用BFS来找,并且更新残留网络的值(涉 ...
- HDU 1532 Drainage Ditches(最大流 EK算法)
题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=1532 思路: 网络流最大流的入门题,直接套模板即可~ 注意坑点是:有重边!!读数据的时候要用“+=”替 ...
- [讲解]网络流最大流dinic算法
网络流最大流算法dinic ps:本文章不适合萌新,我写这个主要是为了复习一些细节,概念介绍比较模糊,建议多刷题去理解 例题:codevs草地排水,方格取数 [抒情一下] 虽然老师说这个多半不考,但是 ...
- vector实现最大流EK算法
序: 在之前的文章中实现了不利用STL实现EK算法,效率也较高.这次我们企图简化代码,减少变量的使用与手写模拟的代码. 注意:vector等STL的container在不开O2优化的时候实现同一个效果 ...
- 【转】最大流EK算法
转自:http://www.cnblogs.com/kuangbin/archive/2011/07/26/2117636.html 图-1 如图-1所示,在这个运输网络中,源点S和汇点T分别是1,7 ...
随机推荐
- MySql EF事务using不会自动 Rollback的bug
EF to MySql一般都是用using最后Commit,一直以为最后没Commit,当using调用Dispose会自动Rollback,没想到这儿有个坑,mysql有个bug并不会Rollbac ...
- NetCore + Mysql CodeFirst 生成数据库
首先定义领域的模型类,然后配置下面的一些东西,最后执行类 1. 新建Context 继承自 DbContext public class EFProjectContext : DbContext { ...
- mysql中id值被重置的情况
MySQL中,如果你为一张使用了innodb引擎的表指定了一auto_increment列,那么这张表会有一个auto_increment计数器,专门记录当前auto_increment的相关值,用来 ...
- 不要再问我Java程序是怎么执行的了!
什么是Java虚拟机? 要弄明白Java程序的执行过程首先要了解一下Java虚拟机 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机有自己完善的硬体架构, ...
- .Net Core 防止跨站点请求伪造
一.在From 表单中生成 antiforgery 令牌 1. ASP.NET Core MVC 和 Razor 页模板中的窗体的所有生成 antiforgery 令牌,唯一且不可预测.服务器先发送到 ...
- spring boot 配置mybatis plus 控制台打印sql
spring boot 版本2.1.5 mybatis plus 版本3.1.1 aplication.properties中添加 logging.level.com.demo.system.mapp ...
- 打开pycharm,提示invalid Log Path【已解决】
问题:打开pycharm,提示invalid Log Path 解决: 网上其他方法都说重装,这个成本有点高,所以我不去尝试. 因为我下载的是免安装版,所以使用时生成的文件是后来才生成的,所以我尝试 ...
- MFC在一个工程中启动其他工程的exe文件
说明:有的时候把两个工程合并,但是偷懒不想在工程中添加代码,所以想到了这个办法,仅限偷懒哈哈哈哈 方法:新建一个主程序,在主程序的界面中添加按钮,在按钮的程序代码中添加以下语句: void CMain ...
- rabbitmq升级新版本后,需要新建用户。新版本默认禁止别的机器用guest用户访问。
rabbitmq升级新版本后,需要新建用户.新版本默认禁止别的机器用guest用户访问.
- 能访问的谷歌 http://209.116.186.231/
能访问的谷歌 http://209.116.186.231/