网络流 最大流最小割与最小费用流
目录
【镇楼】
【引入】
【基本定义和概念】
【最大流算法】
【最小割】
【最小费用最大流】
【引用】
【镇楼】
天啦真的好好懂!!!麻麻再也不用担心我的网络流学习啦!!!
【引入】
首先,我们来看一个网络流图实例。
最大流问题:给定指定的一个有向图,其中有两个特殊的点源点S(Sources)和汇点T(Sinks),每条边有指定的容量(Capacity),求满足条件的从S到T的最大流(MaxFlow)。
那么问题来了,什么是最大流?要满足什么条件?怎么求最大流?
【基本定义和概念】
要知道什么是最大流,首先要知道什么是网络流图和可行流。
网络流图:
在有向图G =(V,E)中:
- 有唯一的一个源点S(入度为0:出发点)
- 有唯一的一个汇点T(出度为0:结束点)
- 图中每条弧(u,v)都有一非负容量 c(u,v)
满足上述条件的图G称为网络流图。
我们可以把图上的边看做一种管道,管道有最大通过流量的限制,图中的每条边的权值就是所谓的“容量”。
可行流:
每条弧(u,v)上给定一个实数 f(u,v)满足:有0<=f(u,v)<=c(u,v),则f(u,v)称为弧(u,v)上的流量。
如果有一组流量满足条件:
- 源点s:流出量=整个网络的流量
- 汇点t:流入量=整个网络的流量
- 中间点:总流入量=总流出量
那么整个网络中的流量成为一个可行流。
如下图所示,对于一个网络可能有多个可行流:
最大流:在所有可行流之中,流量最大的一个流。
上图的可行流7同时也是最大流,注意最大流可能不止一个。
在最大流问题中,容量c和流量f满足三个性质:
- 容量限制 ( f(u,v) <= c(u,v) )
- 斜对称性 ( f(u,v) = - f(v,u) )
- 流量平衡 (对于除了s,t的任意结点u, )
那么如何求最大流呢?
【最大流算法】
这里介绍一个最简单的算法:Edmonds-Karp算法,即最短路径增广算法,简称EK算法。
EK算法基于一个基本的方法:Ford-Fulkerson方法,即增广路方法,简称FF方法。增广路方法是很多网络流算法的基础,一般都在残留网络中实现。其思路是每次找出一条增广路径,然后沿该条增广路径进行更新(增加)流量,调整流值和残留网络,直到没有增广路径为止。
什么是增广路径?怎么找增广路径?什么是残留网络?
增广路径,就是找到一条从s到t的路径,路径上每条边残留容量都为正。也就是说,只要把残留容量为正的边设为可行边,那么我们就可以用简单BFS得到边数最少的增广路径。
所有的可能的增广路径在一起便构成了残留网络,残留网络=容量网络-流量网络。残留网络也称剩余网络。
怎么更新流量,调整流量和残留网络,即如何增广?最多要增广多少次?
BFS得到增广路径之后,这条增广路径能够增广的流值是路径上最小残留容量边决定的。把这个最小残留容量d加到最大流值上,同时路径上每条边的残留容量值都减去d。最后,路径上每条边的反向边残留容量值都要加上d,为什么呢?
由于残留网络=容量网络-流量网络,容量网络不改变的情况下,由于增广好比给增广路上通了一条流,路径上所有边的流量都加上了d,流量网络中路径上正向边的流量加d,反向边流量减去d,相对应的残留网络就发生相反的改变。
步骤如下:
- 计算最小残留容量MinCap。
- 更新流量。如果(u,v)是正向边,则 f(u,v) = f(u,v) + d;是逆向边,则f(u,v) = f(u,v) - d。
最后的总流量增加了d。
可以证明,最多O(VE)次增广可以达到最大流,证明略。
如果觉得不好理解,可以结合下面的图片示例:
可以证明,可行流为最大流,当且仅当不存在新的增广路径。
【模板】
裸题:POJ1273 Drainage Ditches
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=205;
int n,m,max_flow;
int flow[N][N],cap[N][N]; //flow记录流量,cap记录容量
int pre[N],res[N]; //pre记录父亲,res记录残余容量bool bfs(int s,int t) //是否有增广路
{queue <int> q;memset(res,0,sizeof(res));res[s]=inf; //源点残余流量无限q.push(s); //从源点开始进行BFS找增广路径while(!q.empty()){int u=q.front(); q.pop();for(int v=1;v<=m;v++){if(!res[v]&&cap[u][v]>flow[u][v]){ //没被访问过,且容量大于流量pre[v]=u;res[v]=min(res[u],cap[u][v]-flow[u][v]); //更新最小残留容量if(v==t) return 1;q.push(v);}}}return 0;
}int EK(int s,int t)
{int max_flow=0;memset(flow,0,sizeof(flow));while(bfs(s,t)){for(int u=t;u!=s;u=pre[u]){flow[pre[u]][u]+=res[t]; //更新正向边流量flow[u][pre[u]]-=res[t]; //更新反向边流量}max_flow+=res[t]; //更新最大流量}return max_flow;
}int main()
{while(~scanf("%d%d",&n,&m)){memset(pre,0,sizeof(pre));memset(cap,0,sizeof(cap));while(n--){int u,v,w; scanf("%d%d%d",&u,&v,&w);cap[u][v]+=w; //重边看作一条边}printf("%d\n",EK(1,m));}
}
【最小割】
有一个跟最大流密切相关的问题:最小割。
如上图所示,把所有顶点分成两个集合S和T=V-S,其中源点s在集合S中,汇点t在集合T中中。如果把“起点在S中,终点在T中”的边全部删除,就无法从s到达t了,这样的集合划分(S,T)称为一个s-t割,它的容量定义为:c(S,T)=,即起点在S中,终点在T中的所有边的容量和。
最大流最小割定理:
最大流最小割定理(Maximum Flow, Minimum Cut Theorem):网络的最大流等于最小割。
具体的证明就不展开了,反正学了也会忘,记住就行。
让我们继续回到上图。从s到t的水流必然通过跨越S和T的边,所以从s到t的净流量等于:
注意这里的割是任取的,因此得到了一个重要结论:对于任意s-t流f和任意s-t割(S,T),有 。
我们来看残留网络中没有增广路径的情况。既然不存在增广路径,在残留网络中s和t并不连通。当BFS没有找到任何s-t道路时,把已标号结点(a[u]>0的结点u)集合看成S,令T=V-S,则在残留网络中S和T分离,因此在原图中跨越S和T的所有弧满载,且没有从T回到S的流量,因此 成立。
前面说过,对于任意的 f 和(S,T),有 ,而此处又找到了一组让等号成立的 f 和(S,T)。这样,便同时证明了增广路定理和最小割最大流定理:在增广路算法结束时, f 是s-t最大流,(S,T)是s-t最小割。
【最小费用最大流】
还是找增广路的思想,但是这次我们找花费最小(即s到t费用最小)的增广路。
怎么实现呢?最短路算法!
也就是说我们在找增广路时,把bfs换成spfa就可以了!
那么代码实现起来就很简单了
以P3381 【模板】最小费用最大流为例
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 5010;
const int MAXM = 100010;
struct Edge { //邻接表存边 fl是边流量(flow) co是边单位流量的费用(cost)int next,to,fl,co;
} e[MAXM];
int first[MAXN],dis[MAXN],ef[MAXN],num[MAXN],pre[MAXN],que[MAXN << 3];//que就是队列
//pre:当前增广路某点是从哪个点来的(前驱)
short o[MAXN];
int n,s,t,tot = 1,ansf,ansc;
void add(int x,int y,int z,int w)
{//存边与普通最大流差不多 就是费用倒过来时取相反数 即可e[++tot].next = first[x],first[x] = tot,e[tot].to = y,e[tot].co = w,e[tot].fl = z;e[++tot].next = first[y],first[y] = tot,e[tot].to = x,e[tot].co = -w;
}
short spfa()
{ //bfs改成SPFAmemset(dis,0x7f,sizeof(dis)); //同SPFA里的 此时(增广x次后)流到该点的最小费用 因此要更新memset(ef,0x7f,sizeof(ef)); //可改成 ef[s] = INF 反正源点不变然后流可以覆盖//此时 流到某点时剩余的流量(和HLPP的概念差不多 就是挤到某点的流量)//判断某点是否在队列里 然而根据SPFA的原理此条可略int h = 0,tail = 1;que[1] = s;dis[s] = 0;pre[t] = 0;while (h < tail){ int p = que[++h];o[p] = 0;for (int a = first[p],b = e[a].to ; a ; a = e[a].next,b = e[a].to)if (e[a].fl && dis[p] + e[a].co < dis[b]){dis[b] = dis[p] + e[a].co;pre[b] = p; //更新当前点的前驱num[b] = a; //存当前边的编号 通过前驱找点可以找到该边 然后在主程序里可以更新该边的流量ef[b] = min(ef[p],e[a].fl); //挤流量 取小的 然后以此继续推if (!o[b]) o[b] = 1,que[++tail] = b; //p点连接的b点如果没在队列里 压进去}}return pre[t];
//返回前驱 为什么不返回流量? 此处原本是Dinic的bfs 是看有无增广路的 流量存到ef里了
//如果前驱没更新到说明没增广路了 这也是pre[t]要初始化的原因
}
int main()
{int m,x,y,z,w;scanf("%d%d%d%d",&n,&m,&s,&t); while (m--)scanf("%d%d%d%d",&x,&y,&z,&w),add(x,y,z,w); while (spfa()){ ansf += ef[t]; //答案的流加上ansc += ef[t] * dis[t]; //答案的费用乘上for (int now = t ; now != s ; now = pre[now]){//通过前驱找该增广路经过的所有边 然后更新流量 (原路减流量反向弧加流量)e[num[now]].fl -= ef[t];e[num[now] ^ 1].fl += ef[t];}}printf("%d %d\n",ansf,ansc); return 0;
}
【待补充】
【引用】
- 网络流(理论详解)
- 网络流(一) 入门到熟练
- 源ppt链接传送门
网络流 最大流最小割与最小费用流相关推荐
- Algo_网络流,最大流最小割总结, 残留网络性质,知识点总结Tips
catalog 最大流算法 错误点 错误点 残留网络的 可叠加性 流网络的 点和边 最小割定理证明 version_0 version_1 最大流算法 错误点 for( int i = 0; i &l ...
- HDUOJ3549 - Flow Problem(网络流+最大流最小割+模板)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3549 题目大意: 有向图求最大流 解题过程: 关于为什么增加流量时要增加一个反向负流量的边纠结了很久 ...
- hihocoder 网络流二·最大流最小割定理
网络流二·最大流最小割定理 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi:在上一周的Hiho一下中我们初步讲解了网络流的概念以及常规解法,小Ho你还记得内容么? ...
- 流网络的最小割问题c语言,网络流基础-最大流最小割定理
最大流最小割定理,指网络流的最大流等于其最小割. 最大流指符合三个性质的前提下,从S到T能流过的最大流量. 最小割指符合割的定义,最小的割容量. 求最大流: 不断寻找增广路,计算能增加的最小流量,然后 ...
- 洛谷P1344 [USACO4.4]追查坏牛奶Pollutant Control(网络流, 最大流最小割)
初学网络流:http://blog.csdn.net/wzw1376124061/article/details/55001639 最大流最小割:http://blog.csdn.net/wzw137 ...
- 图论 —— 网络流 —— 最小割 —— 平面图与对偶图
[平面图] 对于一个图 G=(V,E),若其重画后,在平面任意两条边的交点除了图中点外,没有其他交点,那么这个图称为平面图 在平面图中,由边包围并且其中不含顶点的区域称为面 包围面 R 的所有边组成的 ...
- 最大流最小割定理(max flow/min cut theory)
百度文库里面有个地址,讲的比较详细. http://wenku.baidu.com/link?url=gPXhYCduLNgZaOkKIltNDAgPGwuMTpRX7a0utvVFuqDAP9o1j ...
- Cable TV Network POJ - 1966 最大流最小割定理 点边转化
最大流最小割定理 任何一个网络的最大流量等于最小割中边的容量之和 即最大流等于最小割 点边转化 节点可以拆为入点和出点 把点的属性添加到入点和出点之间的边上 图的边也可以分两截 在中间加一个节点 把边 ...
- nyoj 677 碟战(最大流最小割定理)
碟战 时间限制:2000 ms | 内存限制:65535 KB 难度:4 描述 知己知彼,百战不殆!在战争中如果被敌人掌握了自己的机密,失败是必然的.K国在一场战争中屡屡失败,就想到自己的某些城市 ...
最新文章
- java 滚轮页面缩放_急..JAVA 在画布上画拖动滚动条可扩大缩小的长方形
- hiredis — Redis 的 C 语言客户端
- windows server 2008相关安装
- java顺序结构类型,Java类的完整构造执行顺序
- hdu1556(Color the ball )
- 【C语言及程序设计】项目2-15:模块化的简单银行系统设计
- [python-thirdLib] Python中第三方的用于解析HTML的库:BeautifulSoup
- java中软填空面试题,通过这9个Java面试题,就可以入职华为啦
- (ISC)² 新增两家 CISSP 官方授权培训合作伙伴
- mate7 刷机 android 7,华为Mate7如何刷机 华为Mate7详细root刷机教程
- 在上海乐字节学习CRM项目管理
- matlab实现转换音频格式文件,mp3到wav的转换
- 关于android百度导航不能出声音的解决办法
- 李彦宏计算机领域的贡献,李彦宏:AI让计算机懂得人、响应人的需求
- SEO 优化--助力网站推广
- wifi6连接不上个别wifi
- divi 相关主题推荐
- mysql 电商实战_SQL电商数据分析实战
- 高精度地图-黑客又要开始装逼了!
- Monkey的11种事件