最大流问题—Ford-Fulkerson

一、概念介绍

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的前驱节点是v
		int 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值