收藏
关注
一个整数集合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 ",0); else printf("%d ",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。