2811:熄灯问题,考点:枚举

原题:http://bailian.openjudge.cn/practice/2811/

描述

有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。

2811:熄灯问题,考点:枚举

 

 在上图中,左边矩阵中用X标记的按钮表示被按下,右边的矩阵表示灯状态的改变。对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。在下图中,第2行第3、5列的按钮都被按下,因此第2行、第4列的灯的状态就不改变。

2811:熄灯问题,考点:枚举

 

 请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道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 }

 

上一篇:brew


下一篇:redis - 为什么要用redis? 参考:https://zhuanlan.zhihu.com/p/59168140