整理的算法模板:ACM算法模板总结(分类详细版)
你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态
10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:
01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:
01111
11001
11001
10100
11011
给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。
输入格式
第一行输入正整数nn,代表数据中共有nn个待解决的游戏初始状态。
以下若干行数据分为nn组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
输出格式
一共输出nn行数据,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若6步以内无法使所有灯变亮,则输出“-1”。
数据范围
0<n≤5000<n≤500
输入样例:
3
00111
01011
10001
11010
11100
11101
11101
11110
11111
11111
01111
11111
11111
11111
11111
输出样例:
3
2
-1
思路:
先确定第一行的状态 ,第一行有5个数,那么用二进制来表示,十进制0~31转换为二进制后,如果当前二进制的某一位为1说明我按了一下这个位置的按钮;所以第一行一共有31种状态,然后现在知道了第一行的状态,递推第二行的状态,枚举第二行的每一个元素a[i][j],如果这个元素对应的上一行(第一行)的元素为0,那么说明这个元素需要按一下,按完之后更新地图;这样枚举第二行的每一个元素就可;对于第三行也是如此,以此类推,处理2~5行;处理完毕之后看最后一行是否全部为1,如果全是1,说明这种方案可行,于是更新按下的次数;最后所有状态枚举完以后取最小的次数,和6比一下即可;
#include <bits/stdc++.h>
using namespace std;
int mp[10][10],py[10][10];
void turn(int x,int y)
{
py[x][y]=py[x][y]^1,py[x][y-1]=py[x][y-1]^1;
py[x][y+1]=py[x][y+1]^1,py[x-1][y]=py[x-1][y]^1;
py[x+1][y]=py[x+1][y]^1;
}
int main()
{
int t;
cin >>t;
char st;
while(t--)
{
getchar();
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++) scanf("%c",&st),mp[i][j]=st-'0';
getchar();
}
int res=10;
for(int k=0;k<32;k++)
{
int cnt=0;
memcpy(py,mp,sizeof py);
for(int j=0;j<5;j++)
{
if((k>>j)&1) turn(1,j+1),cnt++;
}
for(int i=2;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
if(py[i-1][j]==0)
{
turn(i,j);
cnt++;
}
}
}
int flag=1;
for(int i=1;i<=5;i++)
{
if(py[5][i]==0)
{
flag=0;
break;
}
}
if(flag) res=min(res,cnt);
}
if(res>6) res=-1;
cout <<res<<endl;
}
}