您的位置:首页 > 理论基础 > 计算机网络

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

2017-09-25 20:46 465 查看
题目链接: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐