头像

Cyan

四川成都

深度强化学习炼丹师

AcWing-95-费解的开关

AcWing-95-费解的开关

2021-01-27 · 77次阅读 · 原创 · 数据结构与算法

问题

你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字“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

思路

递推。将第一行的所有按法进行遍历,之后再从第一行开始的到第四行从上到下进行翻转,只要当前行的当前位置不为0,则其下一行的对应位置进行翻转。到最后一行后,判断最后一行是否全为1,是1,则翻转成功,更新当前最小操作数。去每一种按法操作数的最小值即为结果。

题解

#include<iostream> #include<cstring> using namespace std; char g[10][10]; int dir[5][2] = {{0,0},{1,0},{0,1},{-1,0},{0,-1}}; int n; const int INF = 1<<30; void turn(int x, int y) { for(int i=0; i<5 ; i++) { int dx = x+dir[i][0]; int dy = y+dir[i][1]; if(dx>=0 && dx <5 && dy>=0 && dy < 5) { //边界判断 g[dx][dy] ^= 1; //'0'和'1'对应48和49,是一对,可用异或取到对方的值 } } } int solve() { int res = INF; char m[10][10]; memcpy(m, g, sizeof g); for(int i = 0; i < 1<<5; i++) { //遍历第一行的所有按法 int cnt = 0; for(int j = 0; j<5; j++) { //将第一行按照i的按法进行按 if((i>>j & 1)) { cnt++; turn(0, j); } } for(int j=0;j<4;j++){ //将前四行按完,最后一行不按 for(int k=0;k<5;k++){ if(g[j][k] == '0'){ cnt++; turn(j+1, k); } } } bool st = true; for(int j=0;j<5;j++){ //判断最后一行是否全为1 if(g[4][j] == '0'){ st = false; break; } } if(st) res = min(res, cnt); memcpy(g, m, sizeof g); } return res>6?-1:res; } int main() { cin>>n; while(n--) { for(int i = 0; i < 5; i++) { cin>>g[i]; } cout<<solve()<<endl; } return 0; }

标题: AcWing-95-费解的开关
链接: https://www.fightingok.cn/detail/33
更新: 2022-09-18 22:33:03
版权: 本文采用 CC BY-NC-SA 3.0 CN 协议进行许可