数位思想

我们知道一个事实,把一个较大的数字转化成数位数组思考,很容易将一个大循环变成很小的循环。

这在解决某些问题的过程中可以发挥很大的作用,将时间压缩。

例子:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=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.

分析:如果直接逐个枚举,那么2^50够我们受的了。但是如果,把那些很大的数字变成数位数组来思考,那么我们可以这样做:将有可能的数字(数字<=X, 且对应的二进制数位上如果X是0,那么该数字也应该是0)转成数位累加起来,最后,数字X二进制位上是1的,累加后的数位数值的最小值就是答案。

例子:

3 7

1 2 4 

数位:

1:0 0 1

2:0 1 0

4:1 0 0

累加后:1 1 1

7:1 1 1

最小的数位数值就是1


下面的代码应该不用看了。。。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=55;
int bit[35];
void solve(int a){
    int dex=1;
    while(a){
        if(a&1) bit[dex]++;
        dex++;
        a>>=1;
    }
}
int length(int a){
    int ans=0;
    while(a){
        a>>=1;
        ans++;
    }
    return ans;
}
int main()
{
    int n,x;
    while(cin>>n>>x){
        memset(bit,0,sizeof(bit));
        int a;
        int len=length(x),ans=0x3f3f3f3f;
        for(int i=1;i<=n;i++){
            scanf("%d",&a);
            if(a<=x) {
                bool flag=1;
                for(int i=1;i<=len;i++){
                    if(!(x&(1<<(i-1)))&&(a&(1<<(i-1)))){ flag=0;break; }
                }
                if(flag)solve(a);
            }
        }

        for(int i=1;i<=len;i++){
            if(x&(1<<(i-1))){
                ans=min(ans,bit[i]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(数位)