AcWing 95. 费解的开关

原题链接:活动 - AcWing


看了前几篇题解和y总的视频讲解,总有一个奇怪的点搞不明白,为什么说第一行确定了,第二行就确定了,第三行接下来也就确定了。

不做一个假设本蒟蒻怎么会明白(((

假设O是按过的按钮,X是没被按过的按钮。

1、那么对于这些按钮来说,显而易见不管用什么顺序去按,只要按对了就行。

2、对于这幅图来说第一行正确的状态是10010,也就是说这群按钮成功的解的第一行必定按过第一行的那两个按钮。

3、按按钮是十字形状的,也就是说第一行的亮暗同样取决于第二行的按钮。

对于这幅图来说,当我们已经发现了第一行的正确状态之后,正确的按下第一行的那两个按钮,你就会发现第二行的按钮已经被确定了。

因为如果你再去按第二行的按钮,就会导致第一行的亮暗错误。

以此类推后面也都被确定了。

所以现在问题就被简化成,如何去找出第一行正确的状态。

解空间发现每个按钮只能按或者不按,那枚举一下第一行所有情况(包含错误情况+正确情况)就好了(((

AC代码


#include <bits/stdc++.h>
using namespace std;

int n,a[6][6],b[6][6],ans;
bool t[6];
string s[6];
void turnLight(int x,int y){
    b[x][y]^=1;//半加运算
    if(x-1>=1)b[x-1][y]^=1;
    if(x+1<=5)b[x+1][y]^=1;
    if(y+1<=5)b[x][y+1]^=1;
    if(y-1>=1)b[x][y-1]^=1;
}
void dfs(int y,bool turn){
    t[y]=turn;
    if(y+1<=5){//画树
        dfs(y+1,true);
        dfs(y+1,false);
    }else{//一条走到底的空间树情况
        int step=0;
        for(int i=1;i<=5;++i){//毛一个a过来用
            for(int j=1;j<=5;++j){
                b[i][j]=a[i][j];
            }
        }
        for(int j=1;j<=5;++j){//第一行的情况
            if(t[j])turnLight(1,j),step++;
        }
        for(int i=2;i<=5;++i){//根据第一行按接下来的2345行
            for(int j=1;j<=5;++j){
                if(b[i-1][j]==0)turnLight(i,j),step++;
            }
        }
        for(int i=1;i<=5;++i){//如果存在一个暗的那就不是这个情况
            for(int j=1;j<=5;++j){
                if(b[i][j]==0)return;
            }
        }
        ans=min(ans,step);//找出所有情况里面的最小值
    }
}
inline void work(){
    for(int i=1;i<=5;++i)
        cin>>s[i];
    for(int i=1;i<=5;++i)
        for(int j=0;j<5;++j)
            a[i][j+1]=s[i][j]-48;

    ans=INT_MAX;
    dfs(1,true);
    dfs(1,false);

    cout<<(ans>6?-1:ans)<<endl;
}

int main(){
    cin>>n;
    while(n--)work();
    return 0;
}

82ms跑过,比y总三年前的172ms略快一点。

此处递归和解空间树相结合,不懂的可以参考

★深度优先搜索+解空间树+递归,三合一详解_zzc大魔王的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值