HDU3829Cat VS Dog(二分图最大独立集)

题目描述:

动物园有N只猫,M只狗,P个小孩。每个小孩都有自己喜欢的动物和讨厌的动物,如果他喜欢狗,那么就讨厌猫,如果他讨厌猫,那么他就喜欢狗。某个小孩能开心,当且仅当他喜欢的动物留在动物园和讨厌的动物不在动物园里面。现让管理员通过带走某些动物,问最多能使多少个孩子开心。



解题思路:

题目有一个关键点,孩子喜欢猫,必然不喜欢狗,反之。 即猫和猫之间,狗和狗之间一定不存在矛盾关系,符合二分图的概念。


如何建立二分图:
若甲小孩喜欢的动物与乙小孩讨厌的动物一样,或者甲小孩讨厌的动物与乙小孩喜欢的动物一样,那甲乙之间就存在着排斥关系,则他们之间连接一条边。

建立完二分图之后,相当于求二分图的最大独立集 = 顶点数 - 最大匹配数。


因为小朋友与小朋友是没有差别的,而二分图必须要求是2个集合,现在只有一个小朋友的集合,那么我们可以用到拆点的思想,把每个小朋友拆成2个小朋友,这样在求最大匹配的时候除以2就可以了。(相当于匹配了2次~。~)。如果你拆点后,就必须建立双向边,比如1和2之间有矛盾,你不能只建立1-2矛盾边,必须还建立2-1矛盾边。


综合上述分析,只要建立双向边,求出最大匹配/2,然后用孩子的个数减去就是答案。


#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
string like[maxn], dislike[maxn];
int head[maxn], match[maxn], child, tol;
bool vis[maxn];
struct Edge
{
    int to, nxt;
}edge[maxn*maxn];
void addedge(int u, int v)
{
    edge[tol].to = v;
    edge[tol].nxt = head[u];
    head[u] = tol++;
}
bool Match(int u)
{
    for(int i = head[u]; i != -1; i = edge[i].nxt)
    {
        int v = edge[i].to;
        if(!vis[v])
        {
            vis[v] = true;
            if(match[v] == -1 || Match(match[v]))
            {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}
int hungary()
{
    int ret = 0;
    for(int i = 0; i < child; i++)
    {
        memset(vis, false, sizeof(vis));
        if(Match(i))
            ret++;
    }
    return ret;
}
int main()
{
    int cat, dog;
    while(scanf("%d%d%d", &cat, &dog, &child) != EOF)
    {
        tol = 0;
        memset(head, -1 ,sizeof(head));
        memset(match, -1, sizeof(match));
        for(int i = 0; i < child; i++)
            cin >> like[i] >> dislike[i];
        for(int i = 0; i < child; i++)
            for(int j = 0; j < child; j++)
                if(like[i].compare(dislike[j]) == 0 || dislike[i].compare(like[j]) == 0)
                    addedge(i, j);
        printf("%d\n", child - hungary() / 2);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值