递归与递推算法四—费解的开关

题目描述:
你玩过“拉灯”游戏吗?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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值