(通俗易懂小白入门)网络流最大流——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 ...
随机推荐
- Python开发【第七篇】: 面向对象和模块补充
内容概要 特殊成员 反射 configparser模块 hashlib模块 logging模块 异常处理 模块 包 1. 特殊成员 什么是特殊成员呢? __init_()就是个特殊的成员. 带双下划线 ...
- 提升——树形DP
这里讲提高一点的内容,所以没有树形DP基础的,先看一下基础部分: 浅说——树形DP 闲言不表,看第一题. 这道题是典型的树上最长链问题.(就是一个模板题) 给定一棵树,树上共有N个节点(N<=5 ...
- kafka入门(三)备份
一.相关概念 备份相关的角色 Kafka消息备份分三个角色:分别是Leader副本.Follower副本.ISR集合 Leader副本 负责直接响应client端的读写请求,即和生产者和消费者直接对接 ...
- Java虚拟机详解(三)------垃圾回收
如果对C++这门语言熟悉的人,再来看Java,就会发现这两者对垃圾(内存)回收的策略有很大的不同. C++:垃圾回收很重要,我们必须要自己来回收!!! Java:垃圾回收很重要,我们必须交给系统来帮我 ...
- 快速掌握mongoDB(一)——mongoDB安装部署和常用shell命令
1.mongoDB简介 mongoDB 是由C++语言编写的,是一种分布式的面向文档存储的开源nosql数据库.nosql是Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统 ...
- UVA816 Abbott的复仇 Abbott's Revenge
以此纪念一道用四天时间完结的题 敲了好几次代码的出错点:(以下均为正确做法) memset初始化 真正的出发位置必须找出. 转换东西南北的数组要从0开始. bfs没有初始化第一个d 是否到达要在刚刚取 ...
- restTemplate重定向问题 &cookie问题
最近在做一个转发功能,zuul + ribbon + resttemplate 进行路由.负载.转发的功能 基本准备就绪,在微信自动登陆那遇到了一个坑,ribbon 系统用resttemplate 转 ...
- [leetcode] 11. Container With Most Water (medium)
原题链接 以Y坐标长度作为木桶边界,以X坐标差为桶底,找出可装多少水. 思路: 前后遍历. Runtime: 5 ms, faster than 95.28% of Java class Soluti ...
- Winform 连接Web Service 记录
一般自己控制的项目都会使用webApi,比较少使用WS,感觉要配置一堆东西很繁琐. 场景:多个系统间数据交互. 角色:我们属于下游系统,要把一部分数据格式化后上传到SAP中. SAP提供了一个WS,使 ...
- Python -----函数(基础部分)
函数: 1.定义: 函数是对功能的封装 2.语法: def 函数名 函数体 函数名 函数名的命名规则和变量一样 3.函数的返回值: return,函数执行完毕,不会执行后面的 1.如果函数中不写ret ...