You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

POJ 3692 Kindergarten

一、题目大意

在幼儿园中,有许多小孩。其中有男孩,也有女孩。女孩之间相互认识,男孩之间也相互认识。同时,一些男孩和女孩之间也相互认识,有一天,老师希望从所有人之中选出一些人来玩游戏,这个游戏需要所有的参与者之间相互认识,问老师可以最多找出多少人来玩这个游戏。

二、思路分析

拿到题首先想到了二分图匹配+并查集,however 认识的关系不是传递的,所以 集合 (并查集)不合适。

但这道题显然是一个匹配的问题,一般图的匹配往往十分复杂,即使ACM大赛也很少出,或者可以 将其转化为二分图

这道题里如果我们将男,女分成两部分,认识作为边,不能满足二分图的条件,因为 男-男女-女都是相互认识的,同一边的点,应该彼此没有关系才能是二分图,需要进行转换。

最简单的转化方式即 认识的没有边,不认识的有边,此时构成一个二分图,我们把样例画出来分析一下:

注:创建补图

2 3 3
1 1
1 2
2 3

这个图和我们的结果有什么关系呢?可以看出他的最大匹配数是2,我们要求的是什么?是一个点集,集合中任意两个点都不相连(也就是 任意两个点相互认识),在二分图中称之为 最大独立点集,其值等于 点数-最大匹配数,这张图中就是5-2=3,也就是最多是3个人组成的团队彼此之间都认识。

:这3人团队怎么组成呢?答:全部女生:1',2',3'

为了更直观的论证他的正确性,不妨在考虑两个特殊情况

  • 左图,任意两点都认识,该图的最大匹配数是0,所以5-0=5个点可以选

  • 右图,每个男生都相互认识,每个女生都相互认识,但任何一个男生和女生都不认识,此时最大匹配数是2,有3个点可以选?5-2=3,这三个点即三个女生。

#include <cstdio>
#include <iostream>
#include <cstring>
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;
}