kuangbin专题十 HDU3829 二分图+最大独立集

题意:
动物园有N只猫,M只狗,P个小孩。每个小孩都有自己喜欢的动物和讨厌的动物,如果他喜欢狗,那么就讨厌猫,
如果他讨厌猫,那么他就喜欢狗。当他喜欢的动物留在动物园和讨厌的动物不在动物园里面,小朋友就开心。
现让管理员通过带走某些动物,问最多能使多少个孩子开心。
题解:
最大独立集=顶点数-最大匹配数(最小顶点覆盖)。一开始想着猫和狗联系上,但是发现不对劲,于是想了很久还是没思绪,别人说可以连接人,然后我想了想对哦,可以连接人,怎么连接呢?只要找到小孩A喜欢的动物和小孩B不喜欢的动物相同的时候说明这两个孩子有矛盾,将两个小孩连起来。然后求出最大独立集(这个集合里的小孩没有矛盾)就好了。但是有一个点让我苦思不得其解,为什么要建立双向边,有的时候又不需要建立双向边,一直在想,找了一下以前的题,于是我大胆的总结了一下,是不是求最小点覆盖的时候就是双向的,求最小路径覆盖的时候就是单向的呢?因为所谓最小路径覆盖,是指在一个有向图中,找出最少的几条路径,用它们来覆盖全图,请问我的观点对吗?希望有大佬给出指点,我实在是不懂。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=500+7;
struct node
{
    char c1,c2;
    int x,y;
}a[MAXN];
bool map[MAXN][MAXN];
bool vis[MAXN];
int cx[MAXN],cy[MAXN];
int n,m,p;
int dfs(int u)
{
    for(int v=1;v<=p;v++)
    {
        if(map[u][v]&&!vis[v])
        {
            vis[v]=true;
            if(cy[v]==-1||dfs(cy[v]))
            {
                cx[u]=v;
                cy[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    while(~scanf("%d%d%d",&n,&m,&p))
    {
        memset(map,false,sizeof(map));
        for(int i=1;i<=p;i++)
        scanf(" %c%d %c%d",&a[i].c1,&a[i].x,&a[i].c2,&a[i].y);
//      for(int i=1;i<=p;i++)
//      printf("%c%d %c%d",a[i].c1,a[i].x,a[i].c2,a[i].y);
        for(int i=1;i<=p;i++)
        for(int j=i+1;j<=p;j++)
        if((a[i].c1==a[j].c2&&a[i].x==a[j].y)||(a[j].c1==a[i].c2&&a[j].x==a[i].y))//不能写成两个条件并存的情况,因为这样的话就只是删除了喜欢和不喜欢互相有矛盾的情况,还有喜欢或者不喜欢的情况 
        {
            map[i][j]=map[j][i]=true;
//          printf("%c%d %c%d\n",a[i].c1,a[i].x,a[i].c2,a[i].y);
//          printf("%c%d %c%d\n",a[j].c1,a[j].x,a[j].c2,a[j].y);
        }
        memset(cx,-1,sizeof(cx));
        memset(cy,-1,sizeof(cy));
        int res=0;
        for(int i=1;i<=p;i++)
        {
            memset(vis,false,sizeof(vis));
            res+=dfs(i);
        }
        printf("%d\n",p-res/2);
    }                       
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值