2017 ACM-ICPC 亚洲区(南宁赛区)网络赛:Frequent Subsets Problem

题目链接:https://nanti.jisuanke.com/t/17320

题目翻译:给出一个数n,和一个浮点数a,数n代表全集U = {1,2,...,n},然后给出

M个U的子集,如果一个集合B(是U的子集),M个集合中有至少M*a个集合包含B,

则B这个集合就是一个满足条件的集合,统计U的子集中B这种集合的个数。


比赛的时候想不到什么好的写法,暴力搜索了一波,超时了(意料之中),最后也每

写出来,今晚特来补题,刚搜索题解,看到大佬们用位运算写的,代码那么短,

真心佩服。


对于N个数的集合,其子集,可以从N个里面挑1个,挑2个,。。。挑N个数构成。

C(n,0)+ C(n,1) + C(n,2)  + C(n,3)  + ..... +  C(n,n) = (1+1)^n = 2^n

N个数子集共有2^n种状态,其中每一个数都代表一个状态,意思就是代表一个子集。

对于输入的M个集合。集合的状态就是2^(num-1)的和。

为何这样?

加入现有一个集合:

1 3 6 10

把它算成 2^(1-1) + 2^(3-1) + 2^(6-1) + 2^(10-1)

这个数字可以用2进制表示:

num(数字):                       10    9 8 7 6 5      4 3 2 1

binary bit(二进制位):     1    0      0      0      1      0      0      1      0      1

power(对应的权值):    2^9 2^8  2^7   2^6   2^5  2^4  2^3   2^2   2^1   2^0

对于每个状态,是一个数,它也是一个二进制串,二进制串中为1的位置,代表了

某个数字存在与集合M,当每个数与集合M进行与运算的时候,可以求出同为1的

二进制位,得出的数与当前这个相同,则说明当前数代表的状态是集合M的子集。


AC代码:

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>

using namespace std;

char str[1000];
int M[55];
int main()
{
    int n,m;
    double a;
    scanf("%d %lf ",&n,&a);
    memset(M,0,sizeof(M));
    m = 0;
    while(gets(str))
    {
        int num = 0;
        int len = strlen(str);
        for(int i = 0; i < len; i++)
        {
            if(str[i]==' ')
            {
                M[m] = M[m] + (1<<(num-1));
                num = 0;
            }
            else
            {
                num = num*10 + str[i]-'0';
            }
        }
        M[m++] += (1<<(num-1));
    }
    int temp = ceil(m*a);
    int state = (1<<n);
    int ans = 0;
    for(int i = 1; i <= state; i++)
    {
        int sum = 0;
        for(int j = 0; j < m; j++)
        {
            /**此处i&M[j]必须用括号括住,刚开始我没有扩,一直输出答案就是0,
            急死我了,一直觉得代码没错,但是答案就是输出0.坑爹。**/
            if((i&M[j]) == i)
                sum++;
        }
        if(sum >= temp)
            ans++;
    }
    printf("%d\n",ans);
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值