最大流与最小割问题
来源:互联网 发布:java网上订餐系统视频 编辑:程序博客网 时间:2024/06/04 18:24
对于一个流网络G (V,E),其流量 f 的最大值称为最大流,最大流问题就是求一个流网络的最大流。
增广路定理:当且仅当由当前的流f 压得的残留网络f G 中不存在增广路径时,流f 的流量f 达到最大。
根据增广路定理,我们可以设计出最基本的求最大流的方法,一开始将流网络G (V,E)的流 f 置为零流,即对于(u,v)E时, f (u,v) 0。然后构建残留网络,寻找增广路径增广,再修改残留网络,重复此过程,直到无法找到增广路径。
此方法(之所以不是算法,是因为实现方法很多)称为Ford-Fulkerson 方法。
伪代码如下:
FORD-FULKERSON-METHORD (G, s, t)
1 initialize flow f to 0
2 while there exists an augmenting path p
3 do augment flow f along p
4 return f
1 将各边上流量 f 初始化为 0
2 while 存在一条增广路径 p
3 do 沿路径 p 增广流量 f
4 return f
假设有向网络 G 中边 (i,j) 的容量为 c(i,j) ,当前流量为 f(i,j) ,则此边的剩余流量即为 r(i,j) = c(i,j) - f(i,j) ,其反向边的剩余流量为 r(j,i) = f(i,j) 。有向网中所有剩余流量 r(i,j) > 0 的边构成残量网络 Gf ,增广路径p即是残量网络中从源点 s 到终点 t 的路径。
沿路径 p 增广流量 f 的操作基本都是相同的,各算法的区别就在于寻找增广路径 p 的方法不同。例如可以寻找从 s 到 t 的最短路径,或者流量最大的路径。
最小割问题
最小割是指流网络中容量最小的割。
Ford-Fulkerson 定理(最小割最大流定理):在流网络中,最小割的容量等于最大流的流量。
根据这个定理,我们就可以通过求流网络的最大流来得到最小割。
几种经典最大流算法
最大流算法分为两大类:增广路 (Augmenting Path) 和预流推进重标号 (Push Relabel) 。也有算法同时借鉴了两者的长处,如 Improved SAP
EK算法(Edmonds-Karp)
在无权边的有向图中寻找最短路,最简单的方法就是广度优先搜索 (BFS),E-K 算法就直接来源于此。每次用一遍 BFS 寻找从源点 s 到终点 t 的最短路作为增广路径,然后增广流量 f 并修改残量网络,直到不存在新的增广路径。E-K 算法的时间复杂度为 O(VE2),由于 BFS 要搜索全部小于最短距离的分支路径之后才能找到终点,因此可以想象频繁的 BFS 效率是比较低的。实践中此算法使用的机会较少。
Dinic算法(构造分层网络寻找最短增广路)
首先定义分层网络 AN(f)。在残量网络中从源点 s 起始进行 BFS,这样每个顶点在 BFS 树中会得到一个距源点 s 的距离 d,如 d(s) = 0,直接从 s 出发可到达的点距离为 1,下一层距离为2 ... 。称所有具有相同距离的顶点位于同一层,在分层网络中,只保留满足条件 d(i) + 1 = d(j) 的边,这样在分层网络中的任意路径就成为到达此顶点的最短路径。
Dinic 算法每次用一遍 BFS 构建分层网络 AN(f),然后在 AN(f) 中一遍 DFS 找到所有到终点 t 的路径增广;之后重新构造 AN(f),若终点 t 不在 AN(f) 中则算法结束。DFS 部分算法可描述如下:
Dinic算法模板#include <iostream>#include <string.h>#include <stdio.h>using namespace std;const int INF=2e9;const int mm=999;const int mn=999;int node,s,t,edge;int ver[mm],flow[mm],next[mm];int head[mn],work[mn],dis[mn],q[mn];void init(int _node,int _s,int _t){ node=_node, s=_s, t=_t; for(int i=0;i<node;++i) head[i]=-1; edge=0;}void addedge(int u,int v,int c){ ver[edge]=v,flow[edge]=c,next[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,next[edge]=head[v],head[v]=edge++;}bool Dinic_bfs(){ int i,u,v,l,r=0; for(i=0;i<node;++i) dis[i]=-1; dis[ q[r++]=s ] = 0; for(l=0;l<r;l++) { for(i=head[ u=q[l] ]; ~i ;i=next[i]) if(flow[i] && dis[ v=ver[i] ]<0) { dis[ q[r++]=v ]=dis[u]+1; if(v==t) return 1; } } return 0;}int Dinic_dfs(int u,int exp){ if(u==t) return exp; for(int &i=work[u],v,temp; ~i ;i=next[i]) { if(flow[i] && dis[ v=ver[i] ]==dis[u]+1 && ( temp=Dinic_dfs(v,min(exp,flow[i])) )>0) { flow[i]-=temp; flow[i^1]+=temp; return temp; } } return 0;}int Dinic_flow(){ int ans=0,res,i; while(Dinic_bfs()) { for(i=0;i<node;++i) work[i]=head[i]; while( res=Dinic_dfs(s,INF) ) ans+=res; } return ans;}int main(){ int n,m,u,v,c; while(scanf("%d%d",&m,&n)!=EOF) { init(n+1,1,n); while(m--) { scanf("%d%d%d",&u,&v,&c); addedge(u,v,c); } printf("%d\n",Dinic_flow()); } return 0;}
Improved SAP算法
通常的 SAP 类算法在寻找增广路时总要先进行 BFS,BFS 的最坏情况下复杂度为 O(E),这样使得普通 SAP 类算法最坏情况下时间复杂度达到了 O(VE2)。为了避免这种情况,Ahuja 和 Orlin 在1987年提出了Improved SAP 算法,它充分利用了距离标号的作用,每次发现顶点无出弧时不是像 Dinic 算法那样到最后进行 BFS,而是就地对顶点距离重标号,这样相当于在遍历的同时顺便构建了新的分层网络,每轮寻找之间不必再插入全图的 BFS 操作,极大提高了运行效率。国内一般把这个算法称为 SAP...显然这是不准确的,毕竟从字面意思上来看 E-K 和 Dinic 都属于 SAP,我还是习惯称为 ISAP 或改进的 SAP 算法。
与 Dinic 算法不同,ISAP 中的距离标号是每个顶点到达终点 t 的距离。同样也不需显式构造分层网络,只要保存每个顶点的距离标号即可。程序开始时用一个反向 BFS 初始化所有顶点的距离标号,之后从源点开始,进行如下三种操作:(1)当前顶点 i 为终点时增广 (2) 当前顶点有满足 dist[i] = dist[j] + 1 的出弧时前进 (3) 当前顶点无满足条件的出弧时重标号并回退一步。整个循环当源点 s 的距离标号 dist[s] >= n 时结束。对 i 点的重标号操作可概括为 dist[i] = 1 + min{dist[j] : (i,j)属于残量网络Gf}。
- 最大流与最小割问题
- poj3469(最大流最小割问题)
- HDU 3046 最大流最小割问题
- 最大流与最小割原理
- 最大流与最小割原理
- 最大流最小割与“Graph Cuts”
- 最大流/最小割
- 最大流最小割
- 最大流最小割
- 最大流最小割
- 最大流最小割
- 最大流最小割问题的流量计算问题
- 最大流最小割定理
- 最大流-最小割简介
- 最大流最小割定理
- poj3308 最大流最小割
- 最小割转最大流
- 最大流和最小割
- 微阵列数据分析(Microarray Data Analysis)
- Leetcode: Pascal's Triangle II
- ural 1500 Pass Licenses (dfs+bit)
- iOS开发中对于NSURLRequest的封装
- 【机器学习】机器学习中英文对照以及学习笔记
- 最大流与最小割问题
- A Utopian Simplex Protocol
- MVCC浅析
- 使用共享内存的单机操作单位时间限制类
- 续行符的作用
- Cocos2d-x开发中遇到的问题汇总
- 关于python中的none
- find 后cp或拷贝目录结构
- uva11078