51nod-【1315 合法整数集】

79 篇文章 0 订阅
15 篇文章 0 订阅
题目来源:  TopCoder
基准时间限制:1 秒 空间限制:131072 KB 分值: 10  难度:2级算法题
 收藏
 关注
一个整数集合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

One

对于这种题目,我真是忍无可忍,可以想象我是最后2个数据一直过不了,即直接下载下来
才知道有2种情况没有考虑,开始写题解
进行或运算全是0才为0
对于一个集合是否存在子集合或运算等于x,可以肯定大于x的数都不用考虑
对于x某个二进制为0,与之相对应的该数的这一位也应该为0,又一次进行筛选
我们只需要考虑x某个二进制为1,那么剩下的数对应为也应该为1(最起码有一个为1)
我们统计最少的1,即使需要删除的个数
下面还有2种情况,1、那就是有可能剩下的筛选出来的数本身就不能组合成x,那就是0
2、剩下的数没有一位和x化成二进制是等数位的那种情况肯定也是0
对于这2种情况我是用 if(sum==10000||max<len)进行判断的

别看我的代码不短,但是时间和大部分人都是一样的15MS

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
LL num[60],count2[600]; 
LL ans[600],flag[600]; 
int main()
{
	LL n,x;
	while(~scanf("%lld%lld",&n,&x))
	{
		LL i;
		memset(count2,0,sizeof(count2));
		memset(flag,0,sizeof(flag)); 
		for(i=0;i<n;++i)
			scanf("%lld",&num[i]);
		sort(num,num+n);
		LL pos=upper_bound(num,num+n,x)-num; 
		LL len=0;
		while(x)
		{
			ans[len++]=x&1; 
			x>>=1;
		}
		LL dex=0,n2,k;
		for(i=0;i<pos;++i,dex=0)
		{
			n2=num[i]; 
			while(n2)
			{
				k=n2&1;
				if(ans[dex]==0&&k)
				{
					flag[i]=1;//代表已经筛去 
					break; 
				} 
				n2>>=1;
				++dex;
			} 
		}
		dex=0;
		LL max=0;
		for(i=0;i<pos;++i,dex=0)
		{
			if(!flag[i])
			{
				n2=num[i];
				while(n2)
				{
					++dex; 
					k=n2&1;
					if(k)
						count2[dex]++;
					n2>>=1;
					if(dex>max)
						max=dex;//记录最大数位 
				} 
			} 
		}
		LL sum=10000; 
		for(i=1;i<=max;++i)
		{
			if(count2[i]&&count2[i]<sum)
				sum=count2[i];
		}
		if(sum==10000||max<len)
			printf("0\n"); 
		else
			printf("%lld\n",sum); 
	} 
	return 0;
} </span>
Two
这是网上的代码,和我的思路是一样的,就是别人比我少了20行,但是运行时间一样
可以看看这个,写的很好 
<span style="font-size:18px;">#include<cstdio>
#include<cstring>
int num[60];
int digitx[100],digitnum[100]; 
int main()
{
	int n,x;
	while(~scanf("%d%d",&n,&x))
	{
		int i,len=0,nx=x;
		memset(digitnum,0,sizeof(digitnum)); 
		for(i=0;i<n;++i)
			scanf("%d",&num[i]);
		while(nx)
		{
			digitx[len++]=nx&1;
			nx>>=1; 
		} 
		int dex;
		for(i=0;i<n;++i)
		{
			if((num[i]|x)>x)
				continue;
			 dex=0;
			 while(num[i])
			 {
			 	digitnum[dex++]+=num[i]&1;
				 num[i]>>=1; 
			 } 
		}
		int sum=50;
		for(i=0;i<40;++i)
		{
			if(digitx[i]&&!digitnum[i])
			{
				 sum=0;
				 break;
			} 
			if(digitx[i])
			{
				sum=digitnum[i]<sum?digitnum[i]:sum; 
			} 
		}
		printf("%d\n",sum); 
	} 
	return 0;
} </span>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值