uva 11205 The broken pedometer 枚举子集

题目大意:

一个表示数字的LED管,即使有些坏掉了,也能把所要表达的数字唯一表示出来。问题拓展开来,给出一些由0或1组成的序列,长度是p,问最少用多少个长度可以把这些序列唯一的表示出来。


问题分析:

题目中的数据范围都很小,粗略估计一些,三层for也可以过。直接用二进制枚举它们的子集就可以。然后判断是否符合要求,结果输出最小的那个值。



做这道题目之前看了刘汝佳白书里面的子集生成,里面推荐了3种做法。分别是增量构造法,位向量法和二进制法。前两种方法都是用递归构造的,而二进制法则是直接枚举,本题使用的就是第三种方法。

1:增量构造法,思路是一次选出一个元素放到集合中,用递归的方法构造子集,觉得相对后面两种方法稍难理解些。

2:位向量法,构造一个位向量B[i],而不是直接构造子集本身,B[i]=1,表示i在构造的子集中,等于0,表示不在。学习的时候感觉相比于第一种方法简单许多,而且书上画的解答树的图也帮助理解了许多,把以前对递归不太懂的地方更好理解了,收获很大。

void print_subset(int n,int *B,int cur)
{
    if(cur==n)
    {
        for(int i=0;i<n;i++)
            if(B[i])
            cout<<i<<" ";
        cout<<endl;
        return ;
    }
    B[cur]=1;
    print_subset(n,B,cur+1);
    B[cur]=0;
    print_subset(n,B,cur+1);
}


3,二进制法,一个二进制数当中,从右往左的第i位表示元素i是否在集合中。如二进制0100011000110111代表集合{0,1,2,4,5,9,10,14}。

连个集合A&B,A|B,A^B分别代表集合的交,并和对称差。0代表空集。全集往往声明为(1<<n)-1 ,则A的补集为((1<<n)-1)^A。

代码更加的简单,也很好理解。

<pre name="code" class="cpp">    int n=5;
    for(int i=0;i < (1<<n);i++)
    {
        for(int j=0;j<n;j++)
            if(i&(1<<j))
                cout<<j<<" ";
        cout<<endl;
    }






AC代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#include<cstring>
#include<math.h>
#include<algorithm>
#include<assert.h>
#include<stdlib.h>
#include<stack>
#include<vector>
#include<map>
#include<set>
#define pi acos(-1.0)
typedef long long LL;
using namespace std;
int main()
{
    int t;
    cin>>t;
    int led[110][20];
    int tmp[110][20];
    int flag[70000];
    while(t--)
    {
        int ok,ans;
        int res=999999;
        int p,n,tt;
        cin>>p>>n;
        for(int i=0; i<n; i++)
            for(int j=0; j<p; j++)
                scanf("%d",&led[i][j]);
        for(int i=0; i<(1<<p); i++)
        {
            for(int s=0; s<n; s++)
            {
                tt=0;
                for(int j=0; j<p; j++)
                {
                    if(i&(1<<j))
                        tmp[s][tt++]=led[s][p-j-1];
                }
            }
            memset(flag,0,sizeof(flag));
            ok=1;
            for(int k=0; k<n; k++)
            {
                int sum=0;
                for(int j=0; j<tt;j++)
                {
                    if(tmp[k][j])
                        sum+=1<<j;
                }
                if(flag[sum])
                {
                    ok=0;
                    break;
                }
                else
                    flag[sum]=1;
            }
            if(ok)
            {
                ans=0;
                for(int j=0;j<p;j++)
                {
                    if(i&(1<<j))            //&和 &&千万不要写错。。。
                        ans++;
                }
                res=min(ans,res);
            }
        }
        printf("%d\n",res);
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章