二分图 最小覆盖数 = 最大匹配数、最大独立集 = 总数-最小覆盖集 证明 hdu 1068


二分图有两个定理:最小覆盖数=最大匹配数、最大独立集=总数-最小覆盖集 。


几个基本定义

最小覆盖:即在所有顶点中选择最少的顶点来覆盖所有的边。

最大匹配:二分图左右两个点集中,选择有边相连的两个匹配成一对(每个点只能匹配一次),所能达到的最大匹配数。

最大独立集:集合中的任何两个点都不直接相连。

最小覆盖数=最大匹配数证明:

参考了别的博客,内容如下:

首先,我们要抓住二分图最大匹配后图的特点,此时,不存在增广路。如下图所示,该图为不完整的最大匹配后的二分图:

 

     红点为匹配点,蓝点为未匹配点。

     对于一个点而言,他所连接的点有这三种情况:

     1、只连接了红点;

     2、只连接了蓝点;

     3、连接了红点和蓝点。

     在上面的图中,x与y是所连接的边是匹配边。x连接了红点和蓝点,这个时候,y所连接的点一定没有蓝点,如果有,就是一条新的增广路,那么该图就不是最大匹配图了。

     另一点更明显,就是在最大匹配图中 不会出现一条边同时连接着两个蓝点。

     那么,对于一条边而言,只有两种情况:

     1、两端的点是红点;

     2、两端的点一点是红色,一点是蓝色。

     可知,一条边上,一定有红点,那么,我们就选择红点作为覆盖点。

     对于上面的匹配边xy,我们无论是选择x还是y都可以覆盖xy这条边,但是对于图中的蓝点而言,只能选择x作为覆盖点去覆盖那条边,这样,我们就选择x作为覆盖点,它所覆盖的边中,既包括了与他相连的那些蓝点的边,也包括了xy这条匹配边。因为y点没有蓝色连接点,所以,y不是必须选择的覆盖点,它与那些红点相连的边都可以选择那些红点来覆盖边。所以,对于一条匹配边而言,我们只需要选择其中一个点就可以覆盖完整个二分图里的边了。

    所以最小覆盖点数等于最大匹配。

最大独立集=总数-最小覆盖集证明:


上图,我们用两个红色的点覆盖了所有边。我们证明的前提条件是已经达到最小覆盖。

即条件1.已经覆盖所有边,条件2.所用的点数最小

首先我们来证明蓝色点组成的是一个独立集:如果有两个蓝色点间有边相连,那么这条边则没有被覆盖,则与条件1矛盾。因此是独立集。

再来证明这个独立集最大: 如果我们要再增加这个独立集中的点,则需要把某个红点变成蓝点。而由最小覆盖数=最大匹配数的证明我们知道,每一个红点是最大匹配中的一个匹配点,也就是说每个红点至少连接了一条边。因此当我们将某个红点变成蓝点时,我们需要牺牲的蓝点的个数是大于等于1的。也就是说,我们最多只能找到数量相等的其他独立集,而无法找到数量更大的。因此蓝色点集必定为最大独立集。 蓝色点数 = 总点数 - 红色点数,即最大独立集=总数-最小覆盖集

以hdu 1068为例:


实际上题目让我们求的就是最大独立集。我们将题中所有点写两遍,最后除2就是所求。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<math.h>
using namespace std;
vector<int>link[1005];
bool vis[1005];
int n,match[1005];
bool find(int p)
{
	int i,j,len = link[p].size();
	for(i = 0; i < len; i++)
	{
		if(!vis[link[p][i]])
		{
			vis[link[p][i]] = 1;	
			if(match[link[p][i]] == -1 || find(match[link[p][i]]))
			{
				match[link[p][i]] = p;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
//	freopen("t.txt","r",stdin);
	int i,j,a,b,m,count;
	while(~scanf("%d",&n))
	{
		count = 0;
		for(i = 0; i < n; i++) match[i] = -1;  
		
		for(i = 0; i < n; i++)
		{
			scanf("%d: (%d)",&a,&m);
			link[a].clear();
			for(j = 0; j < m; j++)
			{
				scanf("%d",&b);
				link[a].push_back(b);
			}
		}
		for(i = 0; i < n; i++)
		{
			memset(vis,0,sizeof(vis));
			if(find(i)) count++;
		}
		printf("%d\n",n-count/2);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值