1. 程式人生 > >枚舉2--熄燈問題

枚舉2--熄燈問題

組成 進制 urn images 規則 編程 %d code space

枚舉2--熄燈問題

一、題目:

有一個由按鈕組成的矩陣,其中每行有6個按鈕,共5行。
每個按鈕的位置上有一盞燈。當按下一個按鈕後,該按鈕以及周圍位置(上邊、下邊、左邊、右邊)的燈都會改變一次。
即,如果燈原來是點亮的,就會被熄滅;如果燈原來是熄滅的,則會被點亮。
在矩陣角上的按鈕改變3盞燈的狀態;在矩陣邊上的按鈕改變4盞燈的狀態;其他的按鈕改變5盞燈的狀態。
給你一個所有燈的初始狀態,請你寫一個程序,確定需要按下哪些按鈕,恰好使得所有的燈都熄滅。

輸入:
每個案例由5行組成,每一行包括6個數字。
這些數字以空格隔開,可以是0或1。0表示燈的初始狀態是熄滅的,1表示燈的初始狀態是點亮的。
樣例輸入:
0 0 1 0 1 0
1 0 1 0 1 1
0 0 1 0 1 1
1 0 1 1 0 0
0 1 0 1 0 0

輸出
對每個案例,首先輸出一行,輸出字符串“PUZZLE #m”,其中m是該案例的序號。
接著按照該案例的輸入格式輸出5行,其中的1表示需要把對應的按鈕按下,0則表示不需要按對應的按鈕。
每個數字以一個空格隔開。
樣例輸出:
PUZZLE #1
1 0 0 1 1 1
1 1 0 0 0 0
0 0 0 1 0 0
1 1 0 1 0 1
1 0 1 1 0 1

二、代碼及分析

技術分享

燈泡陣

這裏用6行8列是保證按規定按鈕影響的都是周圍5盞燈的情況,註意,不是7行8列,因為最後一行不會被下一行影響

  1 /*
  2 分析:
  3     方法一:枚舉所有可能
  4         總共5*6=30展燈,所有可能情況為2^30,所以肯定不行
5 方法二:如果局部狀態確定,那麽其余狀態也被確定,那麽我們只用例舉局部狀態即可 6 當第一行的燈光操作之後,我們可以操作第二行的燈光將第一行的燈光全部熄滅,而此時第二行的燈光也是唯一確定的。 7 而第三,第四等行影響不到第一行。同理第二行的燈光確定後,第三行把第二行全部調熄,那麽第三行也是唯一確定的。 8 同理第四行,第五行也是唯一確定的。 9 所以當確定第一行之後,所有的行都是唯一確定的,我們看這個所有燈的唯一確定的情況是否使所有的燈熄滅從而判斷解是否為可行解。 10 其實我們只要判斷第五行即可,因為照之前的規則,前四行肯定是熄燈的。
11 所以我們只用枚舉第一行就可以枚舉所有的情況。 12 情況總數為:2^6,每種情況都要調所有的30盞燈,執行次數為2^6*5*6(下面的代碼編程我們選這種) 13 14 優化: 15 若是把列看成行,情況總數為2^5,執行次數為2^5*6*5 16 17 18 執行2^6的時候,我們可以用6層循環,我們也可以用數字的二進制來表示。我們選擇用數字的二進制來表示。 19 20 21 22 */ 23 24 25 #include <iostream> 26 using namespace std; 27 int puzzle[6][8],press[6][8];//這裏用6行8列是保證按規定按鈕影響的都是周圍5盞燈的情況,註意,不是7行8列,因為最後一行不會被下一行影響 28 29 bool guess(){//這個函數時對第一行每一個的選擇情況進行判斷 30 int c,r;//r表示行,c表示列 31 for(r=1;r<5;r++){ 32 for(c=1;c<7;c++){ 33 //下一行的按鍵是為了把上一行的按鍵整熄,作用每一個燈的有上下左右自己和自己的初始情況 34 //下按=(自己初始狀態+自己按+左按+右按+上按) 35 press[r+1][c]=(puzzle[r][c]+press[r][c]+press[r][c-1]+press[r][c+1]+press[r-1][c])%2; 36 } 37 } 38 for(c=1;c<7;c++){//判斷最後一行是否全部熄燈 39 //如果最後一行各個按鈕對燈的作用與初始狀態保存一致,則說明是熄燈的。 40 //例如如果總的作用為1,表示按一下,如果之前初始狀態也是1,那麽原來亮著的燈就熄了 41 //對一個燈按奇數次的效果和按一次的效果一樣 42 if((press[5][c-1]+press[5][c]+press[5][c+1]+press[4][c])%2!=puzzle[5][c]) 43 return (false); 44 } 45 return (true); 46 } 47 48 void enumerate(){//這個函數枚舉第一行所有的情況 49 int c; 50 bool success; 51 for(c=1;c<7;c++){ 52 press[1][c]=0;//對按鈕方陣初始化 53 } 54 /* 55 press[1][1]:1 c:1 press[1][c]=press[1][1]>1不成立 1 0 0 0 0 0 表示的是第一盞燈開,其余5盞燈熄 56 press[1][1]:2 c:1 press[1][c]=press[1][1]>1成立 press[1][c]=press[1][1]:0 c++:2 press[1][c]=press[1][2]:1 57 press[1][c]=press[1][2]>1不成立 0 1 0 0 0 0 表示的是第二盞燈開,其余5盞燈熄 58 press[1][1]:3 c:1 press[1][c]=press[1][1]>1成立 press[1][c]=press[1][1]:0 c++:2 press[1][c]=press[1][2]:2 59 press[1][c]=press[1][2]>1成立 press[1][c]=press[1][2]:0 c++:3 press[1][c]=press[1][3]:1 60 0 0 1 0 0 0 表示的是第三盞燈開,其余5盞燈熄 61 以此類推 62 63 */ 64 while(guess()==false){ 65 press[1][1]++; 66 c=1; 67 while(press[1][c]>1){ 68 press[1][c]=0; 69 c++; 70 press[1][c]++; 71 } 72 } 73 return; 74 } 75 76 int main(){ 77 freopen("2in.txt","r",stdin); 78 int cases,i,r,c; 79 80 //對周圍按鈕進行初始化 81 for(r=0;r<6;r++) 82 press[r][0]=press[r][7]=0; 83 for(c=1;c<7;c++){ 84 press[0][c]=0; 85 } 86 //錄入數據,燈泡的初始狀態 87 for(r=1;r<6;r++){ 88 for(c=1;c<7;c++){ 89 scanf("%d",&puzzle[r][c]); 90 } 91 } 92 93 enumerate();//進行枚舉判定 94 printf("PUZZLE #1\n"); 95 //輸出結果 96 for(r=1;r<6;r++){ 97 for(c=1;c<7;c++){ 98 printf("%d ",press[r][c]); 99 } 100 printf("\n"); 101 } 102 return 0; 103 }

枚舉2--熄燈問題