费解的开关(递归与递推)

费解的开关

你玩过“拉灯”游戏吗?

25 盏灯排成一个 5×5 的方形。

每一个灯都有一个开关,游戏者可以改变它的状态。

每一步,游戏者可以改变某一个灯的状态。

游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字 1 表示一盏开着的灯,用数字 0 表示关着的灯。

下面这种状态

10111
01101
10111
10000
11011

在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

给定一些游戏的初始状态,编写程序判断游戏者是否可能在 6 步以内使所有的灯都变亮。

输入格式

第一行输入正整数 n,代表数据中共有 n 个待解决的游戏初始状态。

以下若干行数据分为 n 组,每组数据有 55行,每行 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

 为什么要枚举第一行的所有情况

1,第一行确定余下四行的开灯结果是固定的是必然的发生的
2,第一行的五个开关也是可以按动的,不同的按动会有不同的结果共有2的5次方种结果 

 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=6;
char str[N][N],g[N][N];
int dx[]={-1,0,1,0,0},dy[]={0,1,0,-1,0};
// 这个操作是把(x, y)以及上下左右的灯都变成相反的颜色
void turn(int a,int b)
{
    for(int i=0;i<5;i++)
    {
        int x=a+dx[i],y=b+dy[i];
         //如果在边界外边,直接忽略即可
        if(x<0||x>=5||y<0||y>=5) continue;
        g[x][y]^=1; //异或,不同的时候就变成相反的数
       // 0’的ascall码为48,对应的二进制位最右侧为0,
       //与1做异或,对应的最右侧的0变成了1,
       //同时ascall也变成了49,49对应到char类型就是‘1’.同理可分析‘1’^1
    }
}
int main()
{
     int t;cin>>t;
     while(t--)
     {
         for(int i=0;i<5;i++) cin>>str[i];
         int ans=10;
        //这一步是枚举第一行的点击方法,只要第一行固定了,那么满足题意的点击方法就只有一种了。
        //假如第一行是00111, k从0到31进行枚举,如果k = 00001,
        //那么代表g矩阵中第一行的第一个灯要点击一下,
        //第一行变为11111。
        //k不断变大(0变到31)
        //假如第一行是00111, k从0到31进行枚举,如果k = 10001,
        //那么代表g矩阵中第一行的第一个灯和最后一个灯要点击一下,
        //第一行变为11100。

        //之后固定这一行,改变下面的灯看是否能全变亮。
        //这也就是为什么我们copy light, 每一次对k的枚举都会改变light。

         //枚举第一行的32中状态
         //然后在改变第一排的前提下,用第二排改变第一排灯全亮
         //第三排改变第二排 第四排改变第三排 第五排改变第四排
         //第五排没有别的排数可以改变了
         //这时候若是第五排有没有亮的 这种状态就不行
         //若没有,则是去选一个操作次数最小的
         for(int op=0;op<32;op++)
         { 
            //将str复制到g中
             memcpy(g,str,sizeof str);
             //最开始一部都没有动
             int step=0;
             for(int j=0;j<5;j++)
             {
                 //op=多少代表 它的状态 若是13=1+4+8  01101 枚举32中状态
                 if((op>>j)&1)
                 {
                     turn(0,j);
                     step++;
                 }
             }
             for(int i=0;i<4;i++)
             {
                 for(int j=0;j<5;j++)
                 {
                     if(g[i][j]=='0')
                     {
                         turn(1+i,j);
                         step++;
                     }
                 }
             }
             int flag=0;
             for(int j=0;j<5;j++)
             {
                 if(g[4][j]=='0')
                 {
                     flag=1;
                     break;
                 }
             }
          
             if(!flag)
             {
                 if(step<ans) ans=step;
             }
         }
         if(ans>6) cout<<"-1"<<endl;
         else cout<<ans<<endl;
     }
     return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值