二分图有两个定理:最小覆盖数=最大匹配数、最大独立集=总数-最小覆盖集 。
几个基本定义:
最小覆盖:即在所有顶点中选择最少的顶点来覆盖所有的边。
最大匹配:二分图左右两个点集中,选择有边相连的两个匹配成一对(每个点只能匹配一次),所能达到的最大匹配数。
最大独立集:集合中的任何两个点都不直接相连。
最小覆盖数=最大匹配数证明:
参考了别的博客,内容如下:
首先,我们要抓住二分图最大匹配后图的特点,此时,不存在增广路。如下图所示,该图为不完整的最大匹配后的二分图:
上图,我们用两个红色的点覆盖了所有边。我们证明的前提条件是已经达到最小覆盖。
即条件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);
}
}