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;
}



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