二分图匹配算法总结

最近在学习二分图相关算法,以下内容是根据自己的理解,欢迎讨论。

什么是二分图:

       二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

二分图的最大匹配:

       给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。

         求二分图最大匹配可以用最大流或者匈牙利算法。

         最大流求最大二分匹配的算法中,所有边容量为1,增加额外两个节点是,s,t,将s与A集中的顶点相连,t与B集中顶点相连,运行一遍最大流算法即可。以下主要讨论匈牙利算法。

先给出匈牙利算法代码,以hdu1150为例   http://acm.hdu.edu.cn/showproblem.php?pid=1150

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int map[105][105];        //map[i][j]==1;表示A集中i与B集j有联通
int vis[105];             //B集中的访问标记。
int link1[105],n,m,k;     //link1[i]表示B集i匹配的A集中的元素,若link1[i]==-1,则表示i目前没有匹配
bool can(int i)           //寻找A集i的匹配
{
	int j;
	for(j=0;j<m;j++)
	{
		if(!vis[j]&&map[i][j])    //虽然j从0-m扫描,不可能重复访问,但vis数组还是有必要的,原因在于下面的can(link1[j])
		{                         //有了vis数组后can(link1[j])就不可以再找到j。
			vis[j]=1;
			if(link1[j]==-1||can(link1[j]))  //若B中j没有匹配或与j匹配的A中的元素有其他匹配
			{
				link1[j]=i;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	while(scanf("%d",&n),n!=0)
	{
		scanf("%d%d",&m,&k);
		int i;
		int z;
		memset(map,0,sizeof(map));
		for(i=0;i<k;i++)
		{
			int t1,t2;
			scanf("%d%d%d",&z,&t1,&t2);
			if(t1>0&&t2>0)
				map[t1][t2]=1;
		}
		int s=0;
		memset(link1,-1,sizeof(link1));
		for(i=0;i<n;i++)
		{
			memset(vis,0,sizeof(vis));
			if(can(i))
				s++;
		}
		printf("%d\n",s);
	}
}

匈牙利算法可以解决的图的问题:(二分图)
1.图的最小点覆盖数 = 图的最大匹配数;
2.图的最大点独立集 = 图顶点数 - 图的最大匹配数;
3.图的最小路径覆盖数 = 原图的顶点数 - 原图拆点后形成的二部图的最大匹配数;

 1的证明可以用反证法,若最大匹配数M(最小点数)不能覆盖所有边,则存在额外的边e,把e加入可以得到更大的M。矛盾。

最小路径覆盖数:   

       一个A个结点的有向无环图中,路径覆盖就是在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,那么恰好可以经过图中的每个顶点一次且仅一次)。

       路径覆盖与二分图匹配的关系(必须是没有圈的有向图):最小路径覆盖=|A|-分拆后最大匹配数

将A中i结点分拆一个B中i‘,则有限边中i->j 对应i->j'  原图的结点数-A与B的最大匹配数为最小路径覆盖。

证明根据自己的理解:首先将每个点看成一个路径即p->p 则覆盖所有点需要n个路径,在AB匹配中若对于i->j'则原图有i->j;这样选取此边可是路径数-1,因为在AB匹配中一个点对于一条边,所以在原图中一个点也对应一个边,即满足路径覆盖。。所以 原图的结点数-A与B的最大匹配数为最小路径覆盖。

       

         



你可能感兴趣的:(二分图匹配算法总结)