AcWing 95. 费解的开关
你玩过“拉灯”游戏吗?
25 盏灯排成一个 5×5 的方形。
每一个灯都有一个开关,游戏者可以改变它的状态。
每一步,游戏者可以改变某一个灯的状态。
游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字 1 表示一盏开着的灯,用数字 0 表示关着的灯。
下面这种状态
10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:
01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:
01111
11001
11001
10100
11011
给定一些游戏的初始状态,编写程序判断游戏者是否可能在 6 步以内使所有的灯都变亮。
输入格式
第一行输入正整数 n,代表数据中共有 n 个待解决的游戏初始状态。
以下若干行数据分为 n 组,每组数据有 5 行,每行 5 个字符。
每组数据描述了一个游戏的初始状态。
各组数据间用一个空行分隔。
输出格式
一共输出 n 行数据,每行有一个小于等于 6 的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若 6 步以内无法使所有灯变亮,则输出 −1。
数据范围
0<n≤500
输入样例:
3
00111
01011
10001
11010
11100
11101
11101
11110
11111
11111
01111
11111
11111
11111
11111
输出样例:
3
2
-1
分析
刚开始的时候,不知道从什么地方对这道题下手,就看了y总的代码,现在算是明白了。
y总的解法就是,从第一排开始改变灯的状态,第一排的状态确定了,后面四排也就确定了。
第一排有五个灯,所以有六种改变状态的方式
第一种改变零个灯的状态,根据排列组合有一种情况。
第二种改变一个灯的状态,根据排列组合有五种情况。
第三种改变两个灯的状态,根据排列组合有十种情况。
第四种改变三个灯的状态,根据排列组合有十种情况。
第五种改变四个灯的状态,根据排列组合有五种情况。
第六种改变五个灯的状态,根据排列组合有一种情况。
合计:三十二种情况。
用1表示改变这个灯的状态,0表示不改变
这样每种情况都对应一个01序列
比如上面的改变0个灯的状态只有一种情况,对应的序列就是00000
这个表达的意思第一排的五个灯不进行任何操作。
10000,就是只改变第一个灯的状态。
然后在这里y总提供一个简便方法:
每个01序列对应一个十进制数,设这个数为 x
要判断这个01序列y位置是不是1可以用这个式子
x >> y & 1 如果y位置是1的话,这个式子的结果就为1.
for(int op = 0; op < 32; op ++ )
{
memcpy(bk,st,sizeof st);
int step = 0;
for(int i = 0;i < 5; i ++ )
{
if(op >> i & 1)
{
step ++ ;
turn(0,i);
}
}
这样这段代码就可以把前面提到的所有情况都遍历一遍。
for(int i = 0;i < 5; i ++ )
{
if(op >> i & 1)
{
step ++ ;
turn(0,i);
}
}
这里的作用:
比如 op 是 11000
前面提到了1是改变状态,通过上面这段代码,就改变了第一排前两个灯的状态,turn的作用就不说了。
改变状态可以是 灯由关变成打开,也可以是从打开变成关闭。
C++ 代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 6;
char bk[N][N], st[N][N];
int n;
int dx[] = {1,0,-1,0,0}, dy[] = {0,1,0,-1,0};
void turn(int x, int y)
{
for(int i = 0; i < 5 ; i ++ )
{
int a = x + dx[i],b = y + dy[i];
if(a < 0 || b < 0 || b > 5 || a > 5 ) continue;
st[a][b]^=1;
}
}
int main()
{
cin >> n;
while(n--)
{
int res = 10;
for(int i = 0; i < 5 ; i ++ ) cin >> st[i];
for(int op = 0; op < 32; op ++ )
{
memcpy(bk,st,sizeof st);
int step = 0;
for(int i = 0;i < 5; i ++ )
{
if(op >> i & 1)
{
step ++ ;
turn(0,i);
}
}
for(int i = 0; i < 4; i ++ )
for(int j = 0; j < 5 ; j ++ )
{
if(st[i][j] == '0')
{
step ++ ;
turn(i+1,j);
}
}
bool it = false;
for(int i = 0; i < 5; i ++ )
{
if(st[4][i]=='0')
{
it = true;
break;
}
}
if(!it)res = min(res,step);
memcpy(st,bk,sizeof bk);
}
if(res > 6) cout << -1 << endl;
else cout << res << endl;
}
return 0;
}
python 代码
T = int(input())
dx,dy = [0,1,0,-1,0],[1,0,-1,0,0]
def doit(f,x,y):
for i in range(5):
xx,yy = x + dx[i],y + dy[i]
if xx < 0 or xx >= 5 or yy < 0 or yy >= 5:continue
if f[xx][yy] == '0':
f[xx][yy] = '1'
else:
f[xx][yy] = '0'
for i in range(T):
lis = []
for j in range(5):
lis.append(list(input()))
res = 10
for j in range(32):
lis2 = []
for ii in range(5):lis2.append(lis[ii][:])
ans = 0
its = True
for k in range(5):
if (j >> k) & 1:
ans += 1
doit(lis2,0,k)
for k in range(4):
for wk in range(5):
if lis2[k][wk] == '0':
doit(lis2,k+1,wk)
ans += 1
for wk in range(5):
if lis2[4][wk] == '0':
its = False
break
if its and res > ans:
res = ans
if res <= 6:
print(res)
else:
print(-1)
if i != T -1:
ttt = input()