## [$POJ$ $3692$ $Kindergarten$](http://poj.org/problem?id=3692) ### 一、题目大意 在幼儿园中,有许多小孩。其中有男孩,也有女孩。女孩之间相互认识,男孩之间也相互认识。同时,一些男孩和女孩之间也相互认识,有一天,老师希望从所有人之中选出一些人来玩游戏,这个游戏需要所有的参与者之间相互认识,问老师可以最多找出多少人来玩这个游戏。 ### 二、思路分析 拿到题首先想到了二分图匹配+并查集,$however$ **认识的关系不是传递的**,所以 **集合** (并查集)不合适。 但这道题显然是一个匹配的问题,一般图的匹配往往十分复杂,即使$ACM$大赛也很少出,或者可以 **将其转化为二分图**。 这道题里如果我们将男,女分成两部分,认识作为边,不能满足二分图的条件,因为 **男-男**,**女-女**都是相互认识的,**同一边的点,应该彼此没有关系才能是二分图**,需要进行转换。 最简单的转化方式即 **认识的没有边,不认识的有边**,此时构成一个二分图,我们把样例画出来分析一下: > **注:创建补图** ```cpp {.line-numbers} 2 3 3 1 1 1 2 2 3 ``` ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/%7Byear%7D/%7Bmonth%7D/%7Bmd5%7D.%7BextName%7D/20230801163003.png) 这个图和我们的结果有什么关系呢?可以看出他的最大匹配数是$2$,我们要求的是什么?是一个点集,集合中任意两个点都不相连(也就是 **任意两个点相互认识**),在二分图中称之为 **最大独立点集**,其值等于 **点数-最大匹配数**,这张图中就是$5-2=3$,也就是最多是$3$个人组成的团队彼此之间都认识。 > **注**:这$3$人团队怎么组成呢?答:全部女生:$1',2',3'$。 为了更直观的论证他的正确性,不妨在考虑两个特殊情况 ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/%7Byear%7D/%7Bmonth%7D/%7Bmd5%7D.%7BextName%7D/20230801163016.png) - 左图,任意两点都认识,该图的最大匹配数是$0$,所以$5-0=5$个点可以选 - 右图,每个男生都相互认识,每个女生都相互认识,但任何一个男生和女生都不认识,此时最大匹配数是$2$,有$3$个点可以选?$5-2=3$,这三个点即三个女生。 ```cpp {.line-numbers} #include #include #include using namespace std; /* 一群男孩女孩,同性之间都相互认识,但是异性之间只有某些人认识彼此。给出相互认识的异性的各自编号。 求组成一个小队,这个小队里的人都相互认识。问这个小队最多能有多少人。 把相互不了解的人作为边构建二分图,这样题意是选择相互了解的人,那么是选择二分图的最大独立集, 即总端点数-最大匹配(最小点覆盖) */ const int N = 210; int g[N][N]; int n, m, e; int match[N], st[N]; // match表示女生喜欢的男生,st表示女生是否被匹配到 int dfs(int u) { for (int i = 1; i <= m; i++) { if (!g[u][i] && !st[i]) { st[i] = 1; if (match[i] == -1 || dfs(match[i])) { match[i] = u; return 1; } } } return 0; } int main() { int cas = 0; while (~scanf("%d%d%d", &n, &m, &e)) { cas++; if (n == 0 && m == 0 && e == 0) break; // 初始化匈牙利算法匹配数据 memset(match, -1, sizeof match); // 邻接矩阵 memset(g, 0, sizeof g); // g[i][j]=1:表示i与j互相了解 while (e--) { int a, b; scanf("%d%d", &a, &b); g[a][b] = 1; } int ans = 0; // 枚举集合A中的点,n为集合A中点的个数,即男生的个数 for (int i = 1; i <= n; i++) { // 这里每次都需要全部清0 memset(st, 0, sizeof st); if (dfs(i)) ans++; } // 最大独立集 printf("Case %d: %d\n", cas, n + m - ans); } return 0; } ```