1. 程式人生 > 其它 >2811:熄燈問題,考點:列舉

2811:熄燈問題,考點:列舉

原題: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

解法

有三個關鍵:

  1. 每個按鈕最多隻需按下一次,且按的順序對結果無影響。
  2. 第一行中亮的燈,只用按下第二行裡對應的按鈕,最終看最後一行的結果。
  3. 只用列舉第一行的按鈕情況。可以用數字位運算

我自己的寫法:用中間陣列儲存結果

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