2811:熄燈問題,考點:列舉
阿新 • • 發佈:2021-07-08
原題:http://bailian.openjudge.cn/practice/2811/
描述
有一個由按鈕組成的矩陣,其中每行有6個按鈕,共5行。每個按鈕的位置上有一盞燈。當按下一個按鈕後,該按鈕以及周圍位置(上邊、下邊、左邊、右邊)的燈都會改變一次。即,如果燈原來是點亮的,就會被熄滅;如果燈原來是熄滅的,則會被點亮。在矩陣角上的按鈕改變3盞燈的狀態;在矩陣邊上的按鈕改變4盞燈的狀態;其他的按鈕改變5盞燈的狀態。
在上圖中,左邊矩陣中用X標記的按鈕表示被按下,右邊的矩陣表示燈狀態的改變。對矩陣中的每盞燈設定一個初始狀態。請你按按鈕,直至每一盞等都熄滅。與一盞燈毗鄰的多個按鈕被按下時,一個操作會抵消另一次操作的結果。在下圖中,第2行第3、5列的按鈕都被按下,因此第2行、第4列的燈的狀態就不改變。
請你寫一個程式,確定需要按下哪些按鈕,恰好使得所有的燈都熄滅。根據上面的規則,我們知道1)第2次按下同一個按鈕時,將抵消第1次按下時所產生的結果。因此,每個按鈕最多隻需要按下一次;2)各個按鈕被按下的順序對最終的結果沒有影響;3)對第1行中每盞點亮的燈,按下第2行對應的按鈕,就可以熄滅第1行的全部燈。如此重複下去,可以熄滅第1、2、3、4行的全部燈。同樣,按下第1、2、3、4、5列的按鈕,可以熄滅前5列的燈。
輸入
5行組成,每一行包括6個數字(0或1)。相鄰兩個數字之間用單個空格隔開。0表示燈的初始狀態是熄滅的,1表示燈的初始狀態是點亮的。
輸出
5行組成,每一行包括6個數字(0或1)。相鄰兩個數字之間用單個空格隔開。其中的1表示需要把對應的按鈕按下,0則表示不需要按對應的按鈕。
樣例輸入
0 1 1 0 1 0 1 0 0 1 1 1 0 0 1 0 0 1 1 0 0 1 0 1 0 1 1 1 0 0
樣例輸出
1 0 1 0 0 1 1 1 0 1 0 1 0 0 1 0 1 1 1 0 0 1 0 0 0 1 0 0 0 0
解法
有三個關鍵:
- 每個按鈕最多隻需按下一次,且按的順序對結果無影響。
- 第一行中亮的燈,只用按下第二行裡對應的按鈕,最終看最後一行的結果。
- 只用列舉第一行的按鈕情況。可以用數字位運算
我自己的寫法:用中間陣列儲存結果
#include <iostream> #include <cstring> using namespace std; char lights[5][6]; char result[5][6]; char inputs[5][6]; void change(int x, int y) { lights[x][y] = '0' + (1 - (lights[x][y] - '0')); result[x][y] = '1'; if (x - 1 >= 0) lights[x - 1][y] = '0' + (1 - (lights[x - 1][y] - '0')); if (x + 1 <= 4) lights[x + 1][y] = '0' + (1 - (lights[x + 1][y] - '0')); if (y - 1 >= 0) lights[x][y - 1] = '0' + (1 - (lights[x][y - 1] - '0')); if (y + 1 <= 5) lights[x][y + 1] = '0' + (1 - (lights[x][y + 1] - '0')); } int main() { for (int i = 0; i < 5; i++) for (int j = 0; j < 6; j++) cin >> inputs[i][j]; for (int k = 0; k < (1 << 6); k++) { memset(result, '0', sizeof(result)); memcpy(lights, inputs, sizeof(inputs)); for (int j = 0; j < 6; j++) { int temp = (k >> j) & 1; if (temp) change(0, j); } for (int i = 1; i <= 4; i++) { for (int j = 0; j < 6; j++) { if (lights[i - 1][j] == '1') change(i, j); } } bool flag = true; for (int j = 0; j < 6; j++) { if (lights[4][j] == '1') { flag = false; break; } } if (flag) break; } for (int i = 0; i < 5; i++) { for (int j = 0; j < 6; j++) cout << result[i][j] << " "; cout << endl; } }
老師的解法1:只用存一行而不用存整個矩陣。
1 #include <memory> 2 #include <string> 3 #include <cstring> 4 #include <iostream> 5 using namespace std; 6 int GetBit(char c, int i) { 7 // 取c 的第i 位 8 return (c >> i) & 1; 9 } 10 void SetBit(char & c, int i, int v) { 11 // 設定c 的第i 位為v 12 if (v) 13 c |= (1 << i); 14 else 15 c &= ~(1 << i); 16 } 17 void Flip(char & c, int i) { 18 // 將c 的第i 位為取反 19 c ^= (1 << i); 20 } 21 void OutputResult(int t, char result[]) // 輸出結果 22 { 23 cout << "PUZZLE #" << t << endl; 24 for (int i = 0; i < 5; ++i) { 25 for (int j = 0; j < 6; ++j) { 26 cout << GetBit(result[i], j); 27 if (j < 5) 28 cout << " "; 29 } 30 cout << endl; 31 } 32 } 33 int main() { 34 char oriLights[5]; // 最初燈矩陣,一個位元表示一盞燈 35 char lights[5]; // 不停變化的燈矩陣 36 char result[5]; // 結果開關矩陣 37 char switchs; // 某一行的開關狀態 38 int T; 39 cin >> T; 40 for (int t = 1; t <= T; ++t) { 41 memset(oriLights, 0, sizeof(oriLights)); 42 for (int i = 0; i < 5; i++) { // 讀入最初燈狀態 43 for (int j = 0; j < 6; j++) { 44 int s; 45 cin >> s; 46 SetBit(oriLights[i], j, s); 47 } 48 } 49 for (int n = 0; n < 64; ++n) { // 遍歷首行開關的64 種狀態 50 memcpy(lights, oriLights, sizeof(oriLights)); 51 switchs = n; // 第i 行的開關狀態 52 for (int i = 0; i < 5; ++i) { 53 result[i] = switchs; // 第i 行的開關方案 54 for (int j = 0; j < 6; ++j) { 55 if (GetBit(switchs, j)) { 56 if (j > 0) 57 Flip(lights[i], j - 1);// 改左燈 58 Flip(lights[i], j);// 改開關位置的燈 59 if (j < 5) 60 Flip(lights[i], j + 1);// 改右燈 61 } 62 } 63 if (i < 4) 64 lights[i + 1] ^= switchs;// 改下一行的燈 65 switchs = lights[i]; // 第i+1 行開關方案和第i 行燈情況同 66 } 67 if (lights[4] == 0) { 68 OutputResult(t, result); 69 break; 70 } 71 } // for( int n = 0; n < 64; n ++ ) 72 } 73 return 0; 74 }
解法2:使用bitset類
1 #include <string> 2 #include <cstring> 3 #include <iostream> 4 #include <bitset> 5 #include <algorithm> 6 using namespace std; 7 void OutputResult(int t, bitset<6> result[]) // 輸出結果 8 { 9 cout << "PUZZLE #" << t << endl; 10 for (int i = 0; i < 5; ++i) { 11 for (int j = 0; j < 6; ++j) { 12 cout << result[i][j]; 13 if (j < 5) 14 cout << " "; 15 } 16 cout << endl; 17 } 18 } 19 int main() { 20 bitset<6> oriLights[8]; // 最初燈矩陣,一個位元表示一盞燈 21 bitset<6> lights[8]; // 不停變化的燈矩陣 22 bitset<6> result[8]; // 結果開關矩陣 23 bitset<6> switchs; // 某一行的開關狀態 24 int T; 25 cin >> T; 26 for (int t = 1; t <= T; ++t) { 27 for (int i = 0; i < 5; i++) { // 讀入最初燈狀態 28 for (int j = 0; j < 6; j++) { 29 int s; 30 cin >> s; 31 oriLights[i][j] = s; 32 } 33 } 34 for (int n = 0; n < 64; ++n) { // 遍歷首行開關的64 種狀態 35 copy(oriLights, oriLights + 8, lights); 36 switchs = n; // 第i 行的開關狀態 37 for (int i = 0; i < 5; ++i) { 38 result[i] = switchs; // 第i 行的開關方案 39 for (int j = 0; j < 6; ++j) { 40 if (switchs[j]) { 41 if (j > 0) 42 lights[i].flip(j - 1); // 改左燈 43 lights[i].flip(j); // 改開關位置的燈 44 if (j < 5) 45 lights[i].flip(j + 1); // 改右燈 46 } 47 } 48 if (i < 4) 49 lights[i + 1] ^= switchs;// 改下一行的燈 50 switchs = lights[i]; // 第i+1 行開關方案和第i 行燈情況同 51 } 52 if (lights[4] == 0) { 53 OutputResult(t, result); 54 break; 55 } 56 } // for( int n = 0; n < 64; n ++ ) 57 } 58 return 0; 59 }