最大流问题—Ford-Fulkerson

来源:互联网 发布:linux赋予用户权限 编辑:程序博客网 时间:2024/04/29 06:04

一、概念介绍

1、流网络

流网络是一个有向图G=(V,E),图中的每条边(u,v)有一个非负的容量值,表示边(u,v)上可以流进的最大流量。图中有两个特殊的节点:源节点s和汇聚节点t。

流网络有以下几个性质:

容量守恒:流经边(u,v)的流量不能大于(u,v)的容量

流量守恒:除了s和t外,对于其它所有节点都有,流入该节点的流量等于流出该节点的流量

2、最大流

在一个给定的流网络G中,s到t能够通过的最大流量。

3、残存网络

给定一个流网络G和一个流,流的残留网络Gf拥有与原来网络相同的节点。流网络中每条边将对应残留网络中的一条或者两条边,对于流网络中的任意边(u,v),流量为f(u,v),容量为c(u,v):

如果f(u,v)>0,则在残存网络中包含一条容量为f(u,v)的边(v,u)

如果f(u,v)<c(u,v),则残存网络中包含一条容量为c(u,v)-f(u,v)的边(u,v)

4、增广路径

已知一个流网络和一个流f,增广路径p是其残存网络Gf上s到t的一条简单路径


二、Ford-Fulkerson方法

Ford-Fulkerson方法的描述:

将残存网络初始化为原网络

while(残存网络中是否还能找到一条增广路径p)

         cf=增广路径p上值最小的边的值

         更新p上所有的边:(若边(u,v)在p上,则边(u,v)的值-cf,反向边(v,u)的值+cf)


由上述分析可知用Ford-Fulkerson方法求最大流的关键在于寻找残存网络中的增广路径,下面介绍两种算法

1、Edmonds-Karp算法

该算法采用bfs寻找残存网络中的增广路径

Edmonds-karp算法jva模板

int s;//源节点int t;//汇聚节点int n;//节点总个数int[][] graph;//网络图,graph[i][j]=c,表示边(i,j)的容量为c,graph[i][j]==0,表示边(i,j)不存在/** * 判断残余网络rGraph中是否还有增广路径,并将增广路径记录在path中 * @param rGraph * @param path * @return  */boolean hasPath(int[][] rGraph,int[] path){int[] vis=new int[n];Queue<Integer> q=new LinkedList<Integer>();q.add(s);vis[s]=1;while(!q.isEmpty()){int top=q.poll();for(int i=0;i<n;i++){if(vis[i]==0 && rGraph[top][i]>0){q.add(i);vis[i]=1;path[i]=top;if(i==t) return true;}}}return false;}int ek(){int max=0;int path[]=new int[n];//纪录增广路径,path[u]=v表示u的前驱节点是vint rGraph[][]=new int[n][n];//残存网络//*//将残存网络初始化为原网络for(int i=0;i<n;i++){for(int j=0;j<n;j++){rGraph[i][j]=graph[i][j];}}//*//while(hasPath(rGraph, path)){int min=0xffff;//**//找到增广路径上的最小边for(int v=t;v!=s;v=path[v]){int u=path[v];min=Math.min(min, rGraph[u][v]); }//**////***//更新增广路径上的边和其反向边for(int v=t;v!=s;v=path[v]){int u=path[v];rGraph[u][v]-=min;rGraph[v][u]+=min;}//***//max+=min;}return max;}

2、Dinic算法

算法流程

Dinic算法java模板

int s;//源节点int t;//汇聚节点int n;//节点的总个数int dis[];//dis[i]=k:表示节点i属于第k层,dis[i]=-1,表示结点i没出现在层次图中int graph[][];//网络图,graph[u][v]=c,表示边(u,v)的容量为c,graph[u][v]=0,表示边(u,v)不存在int rGraph[][];//残存网络/** * bfs()建立层次网络 */boolean bfs(){int vis[]=new int[n];Arrays.fill(dis, -1);Queue<Integer> q=new LinkedList<Integer>();q.add(s);vis[s]=1;dis[s]=0;while (!q.isEmpty()){int top=q.poll();for(int i=0;i<n;i++){if(vis[i]==0 && rGraph[top][i]!=0){dis[i]=dis[top]+1;q.add(i);}}}if(dis[t]>0) return true;else return false;}/** * 寻找增广路径 * @param x * @param min * @return 0表示没找到增广路径 */int dfs(int x,int min){int a=0;if(x==t) return min;for(int i=0;i<n;i++){if(rGraph[x][i]>0 && dis[i]==dis[x]+1 && (a=dfs(i, Math.min(min, rGraph[x][i])))!=0){rGraph[x][i]-=a;rGraph[i][x]+=a;return a;}}return 0;}int dinic(){rGraph=graph;//初始化残存网络int max=0;int a=0;while(bfs())//bfs计算层次图{while((a=dfs(s,0xffff))!=0)//寻找增广路径{max+=a;}}return a;}


0 0