题目描述:
你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
思路分析:如果用传统的BFS来做的话也可以,但是时间复杂度会很高,但是这里有一种更好的算法,用DP加递归进行不断搜索直到找到最优解。
首先我们可以假设一下将第一排固定住,那么我们如果要改变第一排的状态就要按下第二排的,将第一排的灯的状态都进行到所想要的状态后就要把下一排固定住然后用第三排去改变其状态,依次类推,到第N排就去按下第N+1排的灯。以此类推,直到最后一排,我们只需要判断最后一排的灯是否是我们想要状态即可,如果是则记录下来,否则果断丢弃。
代码如下:
#include
#include
#define INF 1000000
using namespace std;
char lap[5][5];
int ax[5]={-1,1,0,0,0};//采用数组存储要改变的灯的位置
int ay[5]={0,0,-1,1,0};
void turn(int x,int y)//把五个位置的灯的状态翻转
{
for(int i=0;i<5;i++)
{
int a=x+ax[i];
int b=y+ay[i];
if(a>=0&&a<5&&b>=0&&b<5)
lap[a][b]^=1;//异或实现翻转
}
}
int work()
{
int value=INF;
char back[5][5];//备份一下
memcpy(back,lap,sizeof lap);
for(int i=0;i<1<<5;i++)//利用DP每次循环改变不同的按下状态
{
int sum =0;//记录每次状态下的需要按下的总的次数
for(int j=0;j<5;j++)
{
if(i>>j&1)
{
sum++;
turn(0,j);
}
}
for(int j=0;j<4;j++)//根据上一行的状态进行按下
{
for(int k=0;k<5;k++)
{
if(lap[j][k]=='0')
{
sum++;
turn(j+1,k);
}
}
}
bool flag=true;//检验结果的正确性
for(int i=0;i<5;i++)
if(lap[4][i]=='0')
{
flag=false;
break;
}
if(flag)
value=min(value,sum);//取最小值
memcpy(lap,back,sizeof back);
}
if(value>6)return -1;
return value;
}
int main(void)
{
int T;
cin>>T;
while(T–)
{
for(int i=0;i<5;i++)
cin>>lap[i];
cout<<work()<<endl;
}
return 0;
}