1. 程式人生 > >USACO2.2.4 Party Lamps 派對燈 解題報告(列舉)

USACO2.2.4 Party Lamps 派對燈 解題報告(列舉)

Description

在IOI98的節日宴會上,我們有N(10<=N<=100)盞彩色燈,他們分別從1到N被標上號碼。 這些燈都連線到四個按鈕: 按鈕1:當按下此按鈕,將改變所有的燈:本來亮著的燈就熄滅,本來是關著的燈被點亮。 按鈕2:當按下此按鈕,將改變所有奇數號的燈。 按鈕3:當按下此按鈕,將改變所有偶數號的燈。 按鈕4:當按下此按鈕,將改變所有序號是3*K+1(K>=0)的燈。例如:1,4,7... 一個計數器C記錄按鈕被按下的次數。 當宴會開始,所有的燈都亮著,此時計數器C為0。 你將得到計數器C(0<=C<=10000)上的數值和經過若干操作後所有燈的狀態。寫一個程式去找出所有燈最後可能的與所給出資訊相符的狀態,並且沒有重複。

Input

不會有燈會在輸入中出現兩次。 第一行: N。 第二行: C最後顯示的數值。 第三行: 最後亮著的燈,用一個空格分開,以-1為結束。 第四行: 最後關著的燈,用一個空格分開,以-1為結束。

Output

每一行是所有燈可能的最後狀態(沒有重複)。每一行有N個字元,第1個字元表示1號燈,最後一個字元表示N號燈。0表示關閉,1表示亮著。這些行必須從小到大排列(看作是二進位制數)。 如果沒有可能的狀態,則輸出一行'IMPOSSIBLE'。

Sample Input

10
1
-1
7 -1

/*在這個樣例中,有10盞燈,只有1個按鈕被按下。最後7號燈是關著的。*/

Sample Output

0000000000
0101010101
0110110110

/*在這個樣例中,有三種可能的狀態:

所有燈都關著 
1,4,7,10號燈關著,2,3,5,6,8,9亮著。 
1,3,5,7,9號燈關著,2, 4, 6, 8, 10亮著。*/

每個的燈最開始的時候是亮著的,現在按了C次開關,但是不知道每次按的是哪一個,讓求按到最後每盞燈可能的狀態

思路:

現在一共有4種按開關的方式,而每一種其實就只有開/關兩種狀態(因為同一個開關按3次和按一次效果是一樣的),所以我們可以直接列舉每一種按法,然後判斷是否符合條件就行了

這裡我優化了一下,設了一個flag變數,然後按1開關(全部取反)就將flag取反,最後燈的狀態可以用當前燈的狀態^flag得到(相同為0不同為1,flag為0的話異或不變,flag為1的話異或取反)

#include <map>
#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lowbit(a) (a&(-a))
#define _mid(a,b) ((a+b)/2)
#define _mem(a,b) memset(a,0,(b+3)<<2)
#define fori(a) for(int i=0;i<a;i++)
#define forj(a) for(int j=0;j<a;j++)
#define ifor(a) for(int i=1;i<=a;i++)
#define jfor(a) for(int j=1;j<=a;j++)
#define mem(a,b) memset(a,b,sizeof(a))
#define IN freopen("in.txt","r",stdin)
#define OUT freopen("out.txt","w",stdout)
#define IO do{\
    ios::sync_with_stdio(false);\
    cin.tie(0);\
    cout.tie(0);}while(0)
using namespace std;
typedef long long ll;
const int maxn =  1500+10;
const int INF = 0x3f3f3f3f;
const int inf = 0x3f;
const double EPS = 1e-7;
const double Pi = acos(-1);
const int MOD = 1e9+7;
bool a[105];
int ON[105];
int OFF[105];
char bufs[105];
string res[1<<4|1];
bool flag;
int c,con,cof;
int n;
bool getbit(int x,int i) {      //判斷i號開關是否按下
    return (x>>i)&1;
}
void change(int x) {
    if(getbit(x,0)==1)        //按了1號開關
        flag = true;
    ifor(n) {
        if(getbit(x,1)&&i%2)    //按了2號開關
            a[i] = !a[i];
        if(getbit(x,2)&&i%2==0)//按了3號開關
            a[i] = !a[i];
        if(getbit(x,3)&&(i-1)%3==0)//按了4號開關
            a[i] = !a[i];
    }
}
 
bool juge() {
    fori(con)
    if(!(a[ON[i]]^flag))
        return false;
    fori(cof)
    if(a[OFF[i]]^flag)
        return false;
    return true;
}
 
 
int main() {
    IO;
    //IN;
    con = cof = 0;
    int cnt = 0;
    cin >> n >> c;
    ifor(n)
        a[i] = 1;
    do {
        cin >> ON[con];
    } while(ON[con++]!=-1);
 
    do {
        cin >>OFF[cof];
    } while(OFF[cof++]!=-1);
    cof--,con--;
 
    fori(1<<4) {
//這裡是從0000到1111遍歷,能找出開關所有狀態

        int fun = __builtin_popcount(i);    
//編譯器內建函式,用於求i的二進位制數中'1'的個數
        if(fun%2==c%2&&fun<=c) { //奇偶性相等且次數必須少於或等於按開關的次數
            flag = false;
            change(i);
            if(juge()) {
                int buf = 0;
                ifor(n)
                    bufs[buf++] = (a[i]^flag)+'0';
                bufs[buf] = 0;
                res[cnt++] = bufs;
            }
            change(i);
        }
    }
    if(cnt){
    sort(res,res+cnt);
    fori(cnt)
        cout << res[i] << endl;
    }
    else
        cout <<"IMPOSSIBLE" << endl;
    return 0;
}