收藏
关注
一个整数集合S是合法的,指S的任意子集subS有Fun(SubS)!=X,其中X是一个固定整数,Fun(A)的定义如下:
A为一个整数集合,设A中有n个元素,分别为a0,a1,a2,...,an-1,那么定义:Fun(A)=a0 or a1 or ... or an-1;Fun({}) = 0,即空集的函数值为0.其中,or为或操作。
现在给你一个集合Y与整数X的值,问在集合Y至少删除多少个元素能使集合Y合法?
例如:Y = {1,2,4},X=7;显然现在的Y不合法,因为 1 or 2 or 4 = 7,但是删除掉任何一个元素后Y将合法。所以,答案是1.
Input
第一行两个整数N,X,其中N为Y集合元素个数,X如题所述,且1<=N<=50,1<=X<=1,000,000,000. 之后N行,每行一个整数yi,即集合Y中的第i个元素,且1<=yi<=1,000,000,000.
Output
一个整数,表示最少删除多少个元素。
Input示例
5 7 1 2 4 7 8
Output示例
2
#include<bits/stdc++.h>
#include<cstdio>
const int maxn = 55;
const int M = 0x3f3f3f3f;
using namespace std;
int a[maxn];
int vis[maxn];
int dis[maxn];
int logo[maxn];
int k[maxn];
int i,j;
int main()
{
int n,x;
while(scanf("%d %d",&n,&x)!=EOF)
{
int ans = 0;
memset(vis, 0, sizeof(vis));
while(x)
{
vis[ans++] = x%2;
x /= 2;
}
memset(logo, 0, sizeof(logo));
memset(dis, 0, sizeof(dis));
memset(k, 0, sizeof(k));
for(i=0; i<n; i++)
{
scanf("%d",&a[i]);
int flag = a[i];
int d = 0;
while(flag)
{
dis[d++] = flag % 2;
flag /=2;
if(dis[d-1] && !vis[d-1])
logo[i] = 1;
}
}
int num = M;
for(i=0; i<n; i++)
{
if(logo[i])
continue;
int flag = a[i];
int d = 0;
while(flag)
{
k[d++] += flag%2;
flag /= 2;
}
}
for(i=0; i<ans; i++)
{
if(!vis[i])
continue;
num = min(k[i],num);
}
if(num == M)
printf("%d\n",0);
else
printf("%d\n",num);
}
return 0;
}
这道题的思路不难,我的做法是利用or操作对位的影响。
想一下,如果我们把几个二进制数or起来,我只要某一个数在某一位是1,那么结果的这一位就一定是1。
下面举例说明解法:
比如Y={1,2,4,8} X=7
即 Y:1,10,100,1000 X:111
我们先排除掉那些把它们加入or式中一定会出现!=X的数字,这些数字满足(a|X)==X,也就是说如果一个数字,它的某一个位是1,而对应X的那一位是0,则无论这个数和谁相或,结果都一定不会等于X了。我们先从Y集合中去掉这些数,因为这些数恰恰是无论如何都不可能被去掉的,研究它们没有意义。这里去掉1000
然后,统计X的每一个1位对应Y中是1的有几个:
X:1 1 1
100 10 1
如果我们把Y改为{1,11,100}
那么
X:1 1 1
100 11 1、11
我们要向使得任意子集相或都不为X,方法只有一个,那就是破坏掉X的一个1位,这样无论怎么组合都不会==X了,相反如果每一位都有数字保证,那么一定能组合出X。