递推练习之费解的开关

费解的开关

题目链接:

费解的开关

思路:

我们要明白一下三个点:

  1. 一个方块变化两次后就相当于没变
  2. 第一层的灯变化后的状态,实际上就决定了整个的结果(重点)
  3. 遵循第二点,后面的灯该如何开关,都取决于第一层灯的状态,假设g[0][2] = 0,说明第一层第三个灯是关的,那么我们只能改变g[1][2],来打开它。

第二个结论我就不证明了,证明起来相当麻烦,用语言难以表达

分析:

根据第二点,我们要明白,既然第一层的变化决定了这组灯能否在规定的条件内全部打开,那么我们只要根据第一层的开关对下面的开关进行操作,遍历所有第一层可能出现的状态,然后我们需要对每一种状态从上到下遍历1到4层,假设哪个灯是关的,那么对其下面的灯进行操作,从而把他打开,遍历结束后,我们知道,前4层的灯都是开的,所以我们只需要查看最后一行有没有关的灯,如果有说明不能达到条件,如果没有,那判断操作数是否在6以内,同时找出最小的操作数。

在这里插入图片描述

我们先看看代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 6;
int n;
char g[N][N], backup[N][N];

int dx[5] = {0, 0, 0, 1, -1}, dy[5] = {1, -1, 0, 0, 0};//方向数组,对上下左右的位置进行变换

//变化
void turn(int x, int y){
    for(int i = 0; i < 5; i++){
        int a = x + dx[i], b = y + dy[i];
        if(a < 0 || a >= 5 || b < 0 || b >= 5)continue;
        backup[a][b] ^= 1;
    }
}


int main(){
    int T;
    cin >> T;
    while(T--){
        for(int i = 0; i < 5; i++)
            for(int j = 0; j < 5; j++)
                cin >> g[i][j];
        int res = 10;//res用来存储最小操作数,所以先给他一个较大的值
        
        //模拟所有情况,也就是第一层的状态
        for(int state = 0; state < 32; state++){//为什么要用state遍历32次,请大家看下面的图解
            memcpy(backup, g, sizeof g);
            int step = 0;
            bool dark = false;
            //模拟第一层,对第一层进行操作
            for(int i = 0; i < 5; i++){
                if(state >> i & 1){
                    step++;
                    turn(0, i);
                }
            }
            //根据上面对第一层的操作结果后,开始向下遍历
            for(int i = 0; i < 4; i++){
                for(int j = 0; j < 5; j++){
                    if(backup[i][j] == '0'){
                        step++;
                        turn(i + 1, j);
                    }
                }
            }
            //遍历最后一行,查看是否所有的灯都关闭了
            for(int i = 0; i < 5; i++){
                if(backup[4][i] == '0'){
                    dark = true;
                    break;
                }
            }
            //如果关闭说明成功,step与res比较
            if(!dark)res = min(res, step);
        }
        //规定要在六步以内
        if(res > 6)res = -1;
        cout << res << endl;
    }
    return 0;
}

对于第一层大循环的32次遍历的解释:

首先我们知道,第一层的状态决定了整个的结果,所以我们要对第一层的每种状态进行遍历,而第一层有多少种结果呢,很明显是2的5次方,因为一共有五个灯,而每个灯都有变和不变两种选择,5个灯,那就是2的5次方,也就是32种情况,而我们通过0—31这32个数字分别表示32种情况,大家可以在草稿纸上推算一下0-31的二进制数,每一位表示一个灯,为0说明是关的,为1说明是开的。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

友人苏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值