二分图匹配相关算法及例题分析 (二分图类型问题汇总)

二分图最大匹配:

问题描述:给出一个二分图,找一个边数最大的匹配。就是选择尽量多的边,使得选中的边中任意两条边均没有公共点。如果所有的点都是匹配点那就是一个完美匹配。

解决方案:增广路定理

增广路:从一个未匹配的点开始,依次走过未匹配边,匹配边,未匹配边,匹配边,。。。。。。 如果最后的终点是一个未匹配点(即最后一条边是一条未匹配边),那么这条路就是一条增广路。而将增广路上的未匹配边和匹配边进行互换,就会使得匹配边多一条,如图:

由此可知,存在一条增广路就可以将匹配的边数加一。那么一个匹配是最大匹配的充要条件便是不存在增广路。此定理不仅仅适用于二分图。

经典例子:n*m 的棋盘上放着一些棋子,要求保留最多的棋子使得任意两枚棋子都不在同一行或者同一列。这时候可以将二分图的左图表示每一行,右图表示每一列。棋盘(x,y)位置有棋子则表示左图中代表x行的点与右图中代表y列的点有一条边相连。

若两条边同时依赖于同一点则表示两个棋子在同一行或者同一列,最多的棋子数就是最大匹配数。

关于最大匹配数的一些结论:

1,最大匹配数 = 最小顶点覆盖(选取最少的点,使得点集中的点能覆盖所有的边) 

证明:首先要明确的一点是最小顶点覆盖一定不会小于最大匹配数。显而易见,最大匹配中的每一个匹配都是不相邻的一条边,如果最大匹配数为n,那么至少需要n个顶点来覆盖这n条边。

剩下的证明相等,学习时看了许多博客,怎么形容呢,一头雾水,no picture you say a jb? 

两种情况:

2,顶点数 - 最小顶点覆盖 = 最小边覆盖 (边最少的情况下,边集中的边覆盖所有的点)

证明:设顶点数为 n ,最大匹配为 a。因为要使得边最少,所以先选择最大匹配的边(一次可以覆盖两个点) ,剩下的每个点都需要一条边去覆盖,设剩下的为 b = n -2 * a  。而最小边覆盖此时等于 a + b =n - a,证完。

匈牙利算法(二分图最大匹配算法)

给出模板及例题 poj3041

题意:

/*zhizhaozhuo 匈牙利算法模板 && poj3041*/
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3;
int Map[maxn][maxn],vis[maxn],pre[maxn];
void init(){
	memset(Map,0,sizeof(Map));
	memset(pre,0,sizeof(pre));
}
bool find(int x,int m){
	int i,j;
	for(int i=1;i<=m;i++){  //遍历右图 
		if(Map[x][i] && !vis[i]){ //x->i 有边且 i 未被访问 
			vis[i]=1;
			if(pre[i]==0 || find(pre[i],m)){ //i 未匹配且 通过 i 结点可以找到增广路 
				pre[i]=x;
				return true;
			}
		}
	}
	return false;
}
int solve(int n,int m){
	int sum=0;
	for(int i=1;i<=n;i++){  //遍历左图 
		memset(vis,0,sizeof(vis));//清空标记 
		if(find(i,m))sum++;  
	}
	return sum;
}
int main(){
	int n,k;
	scanf("%d%d",&n,&k);
	init();
	while(k--){
		int a,b;
		scanf("%d%d",&a,&b);
		Map[a][b]=1;
	}
	int ans=solve(n,n);
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/love_phoebe/article/details/81543213
今日推荐