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