51nod 1315 合法整数集问题(预处理)

博客探讨了51nod的1315题,这是一个关于合法整数集的二进制问题。通过将集合元素转换为二进制并统计每位1的出现次数,对x的二进制位进行询问,决定删除相应数量的元素来找到最小答案。注意在x某位为0时,要避免包含该位为1的元素。

1315 合法整数集

一个整数集合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

题解:这是一道二进制的题目,只要想到二进制数如果某一位的1无法出现那么这个数就凑不出来的原理。将集合里的所有元素化为二进制然后加起来就知道每一位1出现的次数arr[i],这样只要对x的二进制每一位做一个询问,如果是1的话,尝试将集合中所有可能出现1的数删掉,也就是arr[i]的值,如果是0,则是删掉n-arr[i]个数,然后一直取最小就是答案。数据处理时候要注意在x某位为0的时候,如果该元素为1则删掉,不要加入统计的总数中,考虑例子x=101,有两个元素为111,001就明白了。

代码:
#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<cstring>
#include<cstdio>
#include<utility>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int Max = 1e6+5;
const int mod = 1e9+7;
const int Hash = 10000;
const int INF = 1<<30;
const ll llINF = 1e18;

int n, x, cnt, counter;//cnt记录集合中元素的二进制位数,counter记录x的二进制位数
int arr[40], temp[40], aim[40];
void binary(int a)
{
    while( a )
    {
        temp[cnt++] = a%2;
        a /= 2;
    }
}
int main( )
{
    //freopen("input.txt", "r", stdin);
    while(~scanf("%d%d", &n, &x))
    {
        memset(arr, 0, sizeof(arr));
        memset(temp, 0, sizeof(temp));
        int k;
        counter = 0;
        binary(x);
        counter = cnt;
        memcpy(aim, temp, sizeof(int)*counter);//aim储存x的二进制序列
        for(int i=0; i<n; i++)
        {
            scanf("%d", &k);
            cnt = 0;
            binary(k);
            int flag = 0;
            for(int j=0; j<cnt; j++)
                if(aim[j]-temp[j] == -1)
                    flag = 1;
            if(flag)//这个元素不管怎么做或操作都不会和x相等,所以不要统计在内,否则会判断出错
                continue;
            for(int i=0; i<cnt; i++)
                arr[i] += temp[i];
        }
        int ans = INF;
        for(int i=0; i<counter; i++)
        {
            if(aim[i] == 1)
                ans = min(ans, arr[i]);
            else
                ans = min(ans, n-arr[i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值