NKOJ-1979 投票<最大独立点集>

17 篇文章 0 订阅

P1979【二分图】投票
时间限制 : 10000 MS 空间限制 : 65536 KB
问题描述

    小 k 同学正在玩一个游戏,在游戏中他扮演了一个马戏团的老板,现在小 k同学需要利用马戏团中的A只猫和B只狗举办一次表演,表演之前他让观众进行了投票,投票的类容是:
    我想看到第___号猫/狗的表演,不想看到第___号猫/狗的表演。
    注意到每个观众都是更喜欢猫或更喜欢狗,所以两个空后面一定会被勾上不同的内容。喜欢猫的观众会在第一空后面选择猫,第二空后面选择狗;反之就会在第一空后面选择狗,第二空后面选择猫。对于每一个观众,只有当 TA投票的内容都被满足了(即 TA想看到的动物出场表演,TA不想看到的动物不参与表演)的时候,TA才会来看表演。当然啦,看表演是要付门票钱的,作为马戏团的老板,小 k自然是希望来的人越多越好了。他想找到一个安排动物表演的方案,使得来看表演的观众尽量多。

输入格式
第1行 3 个正整数n、m、k,分别表示猫、狗和观众的数量
第2~k + 1行,每行描述了一个观众的投票内容。
首先输入一个字符 C或 D紧接着是一个数字,表示某个观众想看到的动物,然后是一个空格隔开,接下去又是一个 C或 D 加上一个数字,表示这个观众不想看到的动物,同一行中一定不会出现两个 C或两个 D。

输出格式
输出一行一个正整数,表示小 k在最优的安排下可以吸引多少观众来看表演。

样例输入
1 2 4
C1 D1
C1 D1
C1 D2
D2 C1

样例输出
3

提示
对于 25%的数据,n,m ≤ 10, k ≤ 25 ;
对于 100%的数据,n,m ≤ 300, k ≤ 500

为什么马戏团里只有猫和狗,连只狮子都没有

如果是我我就不去看这个表演

题解

限制条件

首先,这道题目的限制条件不在于满足某个人的要求,而在于每个人的要求之间存在冲突

也就是说,假如某个人喜欢的猫猫狗狗是另一个人不喜欢的
那么这两个人之间就存在冲突,一个人去了,另一个人一定去不了

A喜欢C1,讨厌D1
B喜欢D1,讨厌C2
C喜欢D2,讨厌D1

那么A和B是冲突的,B和C是冲突的
而A和C之间没有任何关系,因为他们不冲突

解题思路

存在冲突的人不能同时出现,所以我们可以将冲突存为边,将人存为点
那么,我们删除一个点时,与它相连的边也会被删掉,当所有边被删掉时,剩下来的就是最大的结果

那么删掉最少几个点就能得到最大独立集呢?
公式:
最大独立点集=顶点个数-最大匹配数

大概的证明思路就是,最大匹配数代表了最少用多少条边就能够将能够用边连接的点连接起来
于是,删除掉这么多条边上的端点之后,能够用边连起来的匹配就没了

此处注意,n条匹配边连接的是2*n个点,我们删去n个点(且这n个点应该都是在同侧的)之后,剩下来的原本被连接的n个点就成了独立点了

以上证明纯属卖萌,如有不当,无视即可

构图

这道题目关键的点不在于找出冲突,而在于如何存冲突
因为我们知道,二分图上面的点和下面的点是不同的

当1和2发生冲突时,存1-2
当2和3也发生冲突是,存2-3
但这样得到的是两条独立的边
我们需要得到的应该是1-2-3,三个点被两条边连起来

所以我们需要找到一个区分方式,使得点与点之间的关系得到完整的正确的连接
同时也要能够得到满足二分图的点的分配方式

此处要注意到一点

喜欢猫的一定不喜欢狗
喜欢狗的一定不喜欢猫

于是我们通过这个可以得知,冲突一定发生在喜欢猫的人和喜欢狗的人之间
所以看马戏团的人就分成了喜欢狗的和喜欢猫的两堆
上层是狗,下层是猫

于是我们就有了存图方式
当喜欢猫的人和喜欢狗的人之间发生冲突时,就在这两个人之间连一条边
跑一遍最大匹配,套一个公式,得解

解题完成

附上代码

#include <iostream>
#include <cstdio>
using namespace std;

int n,m,k,res=0;
int fath[567],went[567];
int star[34567],nxt[34567],ent[34567],all=0;
int ct[567],dg[567],lk[567],ht[567];

void add(int s,int e)
{
    nxt[++all]=star[s];
    star[s]=all;
    ent[all]=e;
}

bool find(int s,int t)
{
    int bian,e;
    for(int bian=star[s],e=ent[bian];bian;bian=nxt[bian],e=ent[bian])
    if(went[e]!=t)
    {
        went[e]=t;
        if(!fath[e]||find(fath[e],t))
        {
            fath[e]=s;
            return 1;
        }
    }
    return 0;
}

int main()
{
    char l,h;int n1,n2,c=0,d=0;
    scanf("%d%d%d\n",&n,&m,&k);
    for(int i=1;i<=k;i++)
    {
        scanf("%c%d %c%d\n",&l,&n1,&h,&n2);
        if(l=='C')ct[++c]=i;
        else dg[++d]=i;
        lk[i]=n1;
        ht[i]=n2;
    }
    for(int a=1,n1=ct[a];a<=c;a++,n1=ct[a])//猫
    for(int b=1,n2=dg[b];b<=d;b++,n2=dg[b])//狗
        if(lk[n1]==ht[n2]||ht[n1]==lk[n2])add(n1,n2);//存在冲突时
    for(int a=1,n1=ct[a];a<=c;a++,n1=ct[a])//最大匹配
        res+=find(n1,a);
    printf("%d",k-res);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值