题目传送门
题意:
每个方格有一盏灯,有亮和灭两种状态
然后每个方格处也有一个按钮,按下这个按钮,会导致 当前方格和其上下左右的方格的灯的状态转变,即 亮 -> 灭,灭 -> 亮
求按按钮的方式
分析:
题目中也说了,按按钮的顺序不影响最后的结果(这是显然的,不说也应该知道)
可能的做法有:
1. 每个按钮有按和不按两种情况,如果直接枚举的话,那就是O(2 ^ 30),显然吃不消
2. 既然没办法枚举所有的按钮,那么就要想办法排除掉一些无用的情况
我们可以先确定下来第一行的按钮状态,共 2 ^ 6 = 64种情况
然后确定第二行的按钮状态。此时不是枚举了,因为只有第二行的按钮才能影响到第一行的灯,因此,如果在第一行的按钮按完之后,还有灯是亮着的,那么这个亮着的灯的下面一个按钮一定要按,否则这个亮着的灯就没办法熄灭了
依次类推后面几行
可知,第二种方案明显减少了很多的枚举数量
其实代码就是枚举模拟了,需要注意很多细节:
#include<bits/stdc++.h>
using namespace std;
//矩阵的大小
int n = 5, m = 6;
//a是初始灯的状态,b是按钮的按压状态
int a[5][6], b[5][6], temp[5][6];
//四个方向
int d[][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
//检查下标是否越界
bool check(int x, int y){
return x >= 0 && x < n && y >= 0 && y < m;
}
//灯的状态翻转
void tot(int x, int y){
temp[x][y] = !temp[x][y];
}
//按压按钮
void press(int x, int y){
//自身的状态翻转
tot(x, y);
//记录按压
b[x][y] = 1;
//影响四个方向
for(int k = 0; k < 4; k++){
int nx = x + d[k][0], ny = y + d[k][1];
if(check(nx, ny)) tot(nx, ny);
}
}
//判断当前是否还有亮灯
bool isOk(){
for(int i = 0; i < n; i++) for(int j = 0; j < m; j++)
if(temp[i][j]) return false;
return true;
}
int main(){
//读入初始灯的状态
for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) scanf("%d", &a[i][j]);
//枚举第一行的按钮状态
for(int i = 0; i < (1 << m); i++){
//每次都操作temp
memcpy(temp, a, sizeof a);
//每次初始时,没有按钮被按,故初始为0
memset(b, 0, sizeof b);
//先把第一行需要按的按钮按了
for(int j = 0; j < m; j++)
if(i >> j & 1) press(0, j);
//枚举后面的每一行,根据上面一行的状态,决定自身是否需要按
for(int k = 1; k < n; k++)
for(int j = 0; j < m; j++)
if(temp[k - 1][j])
press(k, j);
//所有按钮都枚举完了,判断当前按法是否符和要求
if(isOk()) break;
}
//输出
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++)
printf("%d ", b[i][j]);
puts("");
}
return 0;
}
如果觉得不错,不妨点个赞呗!!!