一.原题链接: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); } }