1 题意
给你每个人互相认识的人,然后问最多能找到多少个人都互不认识。其实就是找:最大独立集合!
链接:link。
2 思路
命题:二分图最大独立集合 = 节点数 - 最大匹配数。
证明:略。
2.1 匈牙利算法
算法步骤如下:
- 任取一个匹配 M M M(可以是空集或只有一条边)
- 令 S S S是非饱和点(尚未匹配的点)的集合
- 如果 S = ∅ S=\empty S=∅,则 M M M已经是最大匹配
- 从 S S S中取出一个非饱和点 u 0 u_{0} u0作为起点,从此起点走交错路 P P P(交替属于 M M M和不属于 M M M的边构成的极大无重复点通路或回路)
- 如果 P P P是一个增广路( P P P的终点也是非饱和点),则令 M = M ⊕ P = ( M − P ) ∪ ( P − M ) M=M \oplus P=(M-P) \cup (P-M) M=M⊕P=(M−P)∪(P−M)
- 如果 P P P都不是增广路,则从 S S S中去掉 u 0 u_{0} u0,转到 s t e p 3 step3 step3
2.1.1 时间复杂度分析
每个点都需要走一遍交错路,所以时间复杂度为 O ( n 2 ) \mathcal{O}(n^{2}) O(n2)。
2.1.1 实现
#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
int n,vis[1000],pre[1000];
vector<int> vec[1000];
void clear_vis()
{
for(int i=0;i<n;i++) vis[i]=0;
}
void clear_pre()
{
for(int i=0;i<n;i++) pre[i]=-1;
}
void clear_vec()
{
for(int i=0;i<n;i++) vec[i].clear();
}
int dfs(int root)
{
for(int i=0;i<vec[root].size();i++)
{
int next=vec[root][i];
if(!vis[next])
{
vis[next]=1;
if(pre[next]==-1||dfs(pre[next]))
{
pre[next]=root;
return 1;
}
}
}
return 0;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
{
int root,num;
scanf("%d: (%d)",&root,&num);
for(int i=0;i<num;i++)
{
int tmp;
scanf("%d",&tmp);
vec[root].push_back(tmp);
vec[tmp].push_back(root);
}
}
int sum=0;
clear_pre();
for(int i=0;i<n;i++)
{
clear_vis();
sum+=dfs(i);
}
printf("%d\n",n-(sum>>1));
clear_vec();
}
}