一.原题链接:http://poj.org/problem?id=1466
二.题目大意:一个人可能跟多个人有暧昧,求彼此没有暧昧关系的人最多的集合。
三,思路:其实就是直接给一个图,让你求最大独立点集。但是这是传说中的NP问题。好在只有男的和女的能发生暧昧关系(题目说的)。
建图如下:
1.一个点认为他可能是男的也可能是女的,于是拆为2个点分在2边。
2.如果原来有连线,就2边连线。
然后匈牙利求最大匹配。注意此时求出的匹配的原图的2倍,因为你把每条边都乘以2了。
(有(童鞋)可能会说,我把第一个人先标记为男的或者女的,然后跟他配对的就是另一个性别,我觉得应该也可以,不过我没试过。不过我觉得会比较麻烦,你还要设一个标记数组来标记性别,扫点的时候还要判断它是哪一边的。而且还有个问题,如果出现过以前没配对过的你要把他弄成男的还是女的?)
求得最大匹配之后,有一个结论:
最大匹配 + 最大独立点集 = 顶点数
看到这个结论就懵B了有木有啊。下面来简单说明一下这个结论:
1.概念扫盲:
最大匹配数:最大独立边集,不相邻的边集。
最小覆盖点数:用最小的点覆盖所有的边,也就是说,图中的每条边,都能在最小覆盖点集合里面找到至少一个点,覆盖这条边。
最大独立点集:图中最多的不相邻的点的集合。
2. 结论:
最小覆盖点数 + 最大独立点数 = 顶点数。
这个我会证,
只要证覆盖点数 +独立点数 = 顶点数。
假设u,v相邻且在顶点数-覆盖点集里面,就是u,v是一条边的2个端点,那么说明u,v不在覆盖点集里面,说明u,v决定的边没法被覆盖,矛盾。于是u,v肯定是独立的点,于是顶点数-覆盖点集里面的点都不相邻。
3.结论
最小覆盖点数 = 最大匹配
每个最小覆盖点的点都对应的是一条最大匹配的边。
4.2的结论和3的结论相加,就完了。
四.代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cstdlib>
using namespace std;
const int INF = 0x3f3f3f3f,
MAX_N = 509;
class maxMatch
{
public:
bool connected[MAX_N][MAX_N];
bool visited[MAX_N];
int xMatch[MAX_N], yMatch[MAX_N],
xNum, yNum;
maxMatch()
{
memset(connected, 0, sizeof(connected));
memset(xMatch, -1, sizeof(xMatch));
memset(yMatch, -1, sizeof(yMatch));
}
bool path(int u)
{
int v;
for(v = 0; v < yNum; v++)
if(connected[u][v] && !visited[v]){
visited[v] = true;
if(-1 == yMatch[v] || path(yMatch[v])){
yMatch[v] = u;
xMatch[u] = v;
return true;
}
}
return false;
}
int getRes()
{
int u, res = 0;
for(u = 0; u < xNum; u++){
memset(visited, 0, sizeof(visited));
if(path(u))
res++;
}
return res;
}
};
void readG(int num)
{
maxMatch G;
G.xNum = G.yNum = num;
int i, v, cnt;
for(i = 0; i < num; i++){
scanf("%d: (%d)", &i, &cnt);
while(cnt--){
scanf("%d", &v);
G.connected[i][v] = true;
}
}
printf("%d\n", num - G.getRes()/2);
}
int main()
{
//freopen("in.txt", "r", stdin);
int i, j, num;
while(~scanf("%d", &num)){
readG(num);
}
}