ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-07-08 20:02:48  阅读:228  来源: 互联网

标签:熄灯 2811 开关 int switchs lights 枚举 按钮 include


原题: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 }

 

标签:熄灯,2811,开关,int,switchs,lights,枚举,按钮,include
来源: https://www.cnblogs.com/erable/p/14987889.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有