图的匹配问题与最大流问题(五)——计算二分图的最大匹配

二分图的最大匹配问题第一篇已经说过,下面看看百度百科给的一些解释:

给定一个二分图G,M为G边集的一个子集,如果M满足当中的任意两条边都不依附于同一个顶点,则称M是一个匹配
极大匹配(Maximal Matching)是指在当前已完成的匹配下,无法再通过增加未完成匹配的边的方式来增加匹配的边数。最大匹配(maximum matching)是所有极大匹配当中边数最大的一个匹配。选择这样的边数最大的子集称为图的最大匹配问题。
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
计算二分图最大匹配可以用最大流(Maximal Flow)或者匈牙利算法(Hungarian Algorithm)。

如图,所示,最大匹配为4:.

 


一、最大流方法计算最大匹配
最大流问题已经前面已经解释了很多了,不妨可以回去熟悉下先。 最大流问题Ford-Fulkerson解法

如图所示,对于一个二分图,令已有的边的容量(Capacity)为无穷大,增加一个源点s和一个汇点t,令s和t分别连接二部图中的一个分步,并设置其容量为1。这时得到流网络G’,计算得到的最大流就等于最大二分匹配。

但是,我们还必须回答为什么这种方法是可行的呢?这里有一个简单的证明方法。首先假设,当前流网络有一个最大流,但它对应的不是最大匹配,那么,我们至少还可以向最大匹配中加入一条边,设为(u,v),显然我们还可以增加条增广路径,s->u->v->t。那么就得到一个更大的流,和假设矛盾。所以假设不成立。同理,假设当前有一个最大匹配,其对应的不是最大流,那么至少还存在一条增广路径,假设s->u->v->t。这时就可以增加边(u,v)到最大匹配中,矛盾。
代码就很简单了,构造流网络G‘,然后调用最大流算法即得到结果。因为二分图上任何匹配的势之多为min(L,R)(L代表二分图的左部点集,R代表二分图的右部点集),所以G’中最大流的值为O(V),因此,可以再O(VE‘)=O(VE)的时间内,找出二分图的最大匹配。
二、匈牙利算法
这可是一个大名鼎鼎的算法,它实际上是对最大流算法的一种改进,提高了效率。网上有一篇文章将的很详细,很受用,可以参考: http://imlazy.ycool.com/post.1603708.html。我就不赘述了,直接上代码了,Java实现。
package matchings;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import entry.Edge;
import entry.Node;

import maxflow.FordFulkerson;

public class Matching {

	private double graph[][];
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		double graph[][]={
		{0,0,0,1,1,0},
		{0,0,0,0,1,1},
		{0,0,0,0,0,1},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0},
		{0,0,0,0,0,0}
		};
		double graph2[][]={
				{0,0,0,0,0,1,0,0,0},
				{0,0,0,0,0,1,0,1,0},
				{0,0,0,0,0,0,1,1,1},
				{0,0,0,0,0,0,0,1,0},
				{0,0,0,0,0,0,0,1,0},
				{0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0}
				};
		Matching m=new Matching(graph2);
		int L[]={0,1,2,3,4};
		int R[]={5,6,7,8};
		/*
		double flowNetwork[][]=m.getMaxMachingFromMaxFlow(graph2,L,R);
		
		int length=flowNetwork.length;
		for(int i=0;i0?flowNetwork[i][j]:0.0)+" ");
			}
			System.out.println();
		}*/
		System.out.println(m.getMaxMaching(graph2, L, R));
	}
	public Matching(double graph[][])
	{
		this.graph=graph;
	}
	/**
	 * Let G be a bipartite graph with partite sets U and W such that r=|U|<=|W|.
	 * Then G contains a matching of cardinality r if and only if U is neighborly.(which means that :For any nonempty subset X,N(X)>=X)
	 * 但是这种方法的复杂度为O(2^n),所以可以不如用下面两个算法直接求出最大匹配,然后比对是否是perfect matching
	 * @return
	 */
	public boolean hasPerfectMatching(double graph[][],int L[],int R[])
	{
		
		return false;
	}
	
	/**
	 * 利用最大流算法实现,返回最大流网络
	 * @param graph
	 * @param L
	 * @param R
	 * @return
	 */
	public double[][] getMaxMachingFromMaxFlow(double graph[][],int L[],int R[])
	{
		int length=graph.length;
		double augGraph[][]=new double[length+2][length+2];
		for(int i=0;i getMaxMaching(double graph[][],int L[],int R[])
	{
		int length=graph.length;
		for(int i=0;i rSet=new HashSet();
		Set lSet=new HashSet();
		List list=new ArrayList();
		for(int i=0;i list,Set rSet,Set lSet) {

		int idx=0;
		List oddEdge=new ArrayList();
		List evenEdge=new ArrayList();
		while(result.getParent()!=null)
		{
			Node parent=result.getParent();
			
			if(idx%2==0)
			{
				Edge e=new Edge(parent.nodeId,result.nodeId,0);
				evenEdge.add(e);
				
			}
			else
			{
				Edge e=new Edge(result.nodeId,parent.nodeId,0);
				oddEdge.add(e);
			}
			idx++;
			result=parent;
		}
		/**
		 * 检测第6条性质
		 */
		for(int i=0;i






你可能感兴趣的:(算法,图论)