网络流

网络流是模仿水流解决生活中类似问题的一种方法策略,来看这么一个问题,有一个自来水厂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算法的更多相关文章

  1. 二分图的最大匹配——最大流EK算法

    序: 既然是个图,并且求边数的最大值.那么这就可以转化为网络流的求最大流问题. 只需要将源点与其中一子集的所有节点相连,汇点与另一子集的所有节点相连,将所有弧的流量限制置为1,那么最大流 == 最大匹 ...

  2. 最大流EK算法/DINIC算法学习

    之前一直觉得很难,没学过网络流,毕竟是基础知识现在重新来看. 定义一下网络流问题,就是在一幅有向图中,每条边有两个属性,一个是cap表示容量,一个是flow 表示流过的流量.我们要求解的问题就是从S点 ...

  3. 网络流最大流——dinic算法

    前言 网络流问题是一个很深奥的问题,对应也有许多很优秀的算法.但是本文只会讲述dinic算法 最近写了好多网络流的题目,想想看还是写一篇来总结一下网络流和dinic算法以免以后自己忘了... 网络流问 ...

  4. 初探网络流:dinic/EK算法学习笔记

    前记 这些是初一暑假的事: "都快初二了,连网络流都不会,你好菜啊!!!" from 某机房大佬 to 蒟蒻我. flag:--NOIP后要学网络流 咕咕咕------------ ...

  5. 最大流——EK算法

    一.算法理论 [基本思想] 反复寻找源点s到汇点t之间的增广路径,若有,找出增广路径上每一段[容量-流量]的最小值delta,若无,则结束.在寻找增广路径时,可以用BFS来找,并且更新残留网络的值(涉 ...

  6. HDU 1532 Drainage Ditches(最大流 EK算法)

    题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=1532 思路: 网络流最大流的入门题,直接套模板即可~ 注意坑点是:有重边!!读数据的时候要用“+=”替 ...

  7. [讲解]网络流最大流dinic算法

    网络流最大流算法dinic ps:本文章不适合萌新,我写这个主要是为了复习一些细节,概念介绍比较模糊,建议多刷题去理解 例题:codevs草地排水,方格取数 [抒情一下] 虽然老师说这个多半不考,但是 ...

  8. vector实现最大流EK算法

    序: 在之前的文章中实现了不利用STL实现EK算法,效率也较高.这次我们企图简化代码,减少变量的使用与手写模拟的代码. 注意:vector等STL的container在不开O2优化的时候实现同一个效果 ...

  9. 【转】最大流EK算法

    转自:http://www.cnblogs.com/kuangbin/archive/2011/07/26/2117636.html 图-1 如图-1所示,在这个运输网络中,源点S和汇点T分别是1,7 ...

随机推荐

  1. Cisco packet tracer下dhcp的配置的vlan的应用

    话不多说,先上拓扑图. pc0和pc1分别接在三层交换机的F0/1.F0/2接口,ser接在F0/24接口,用ser用作dhcp的服务器. 0x01:配置server0 先配置server的IP地址. ...

  2. java虚拟机-程序计数器PC Register

    什么是程序计数器? 程序计数器是一块 较小 的内存空间,它可以看做是当前线程所执行的字节码的 行号指示器 :在虚拟机的概念模型里(仅仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解 ...

  3. Python编程菜鸟成长记--A1--01--编程语言介绍

    目录 1.重点知识 2.什么是编程?为什么要编程? 3.有哪些编程语言? 3.1.机器语言 3.2.汇编语言 3.3.高级语言 3.3.1.编译型语言 3.3.2.解释型语言 3.4.小结 4.主流编 ...

  4. 安装Eclipse for MAC 苹果版

    1. 安装Eclipse for MAC 苹果版 2. Thank you for downloading Eclipse If the download doesn't start in a few ...

  5. 从无到有构建vue实战项目(五)

    八.错误总结(一) webpack打包项目识别子组件路径问题 之所以出现了这样的问题是因为在webpack打包项目时,未将此处的子组件路径正确识别: 将此处的carousel改为carousel.vu ...

  6. Json串排序

    最近遇到个很烦人的问题.我现在做的业务有一部分是把之前app服务端的.net接口转译java接口.但是有些之前的接口,一个接口干上十几件事情,返回的json串长达五六几百行.着实看的就让人头大.但是如 ...

  7. python数据库-MongoDB的基本使用(54)

    一.MongoDB 创建数据库 语法:MongoDB 创建数据库的语法格式如下: use DATABASE_NAME 如果数据库不存在,则创建数据库,否则切换到指定数据库. > use Hero ...

  8. mysql+mybatis存储超大json

    1. 场景描述 因前端界面需存储元素较多,切割后再组装存储的话比较麻烦,就采用大对象直接存储到mysql字段中,根据mysql的介绍可以存放65535个字节,算了算差不多,后来存的时候发现: 一是基本 ...

  9. Image Classification

    .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...

  10. C++内存泄漏及检测工具详解

    #include "stdafx.h" #ifdef _DEBUG #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, ...