P2040 打开所有的灯

213 阅读2分钟

题目背景

pmshz在玩一个益(ruo)智(zhi)的小游戏,目的是打开九盏灯所有的灯,这样的游戏难倒了pmshz。。。

题目描述

这个灯很奇(fan)怪(ren),点一下就会将这个灯和其周围四盏灯的开关状态全部改变。现在你的任务就是就是告诉pmshz要全部打开这些灯。

例如

0  1  11  0  01  0  1

点一下最中间的灯【2,2】就变成了

0  0  10  1  11  1  1

再点一下左上角的灯【1,1】就变成了

1  1  11  1  11  1  1

达成目标。最少需要2步。

输出2即可。

输入格式

九个数字,3*3的格式输入,每两个数字中间只有一个空格,表示灯初始的开关状态。(0表示关,1表示开)

输出格式

1个整数,表示最少打开所有灯所需要的步数。

输入输出样例

输入

0 1 1
1 0 0
1 0 1

输出

2

说明/提示

这个题水不水,就看你怎么考虑了。。。。

这道题其实就是一个搜索题,而且只有3*3个数据。

因为开灯这件事情,按钮按一下会发生变换,再按一下就复原了,相当于没有操作,所以最多每个按钮按一次,也就是最多也才就次。

我们只需要用一个深度优先搜索去试探开关灯,每次操作后去检查打开的灯数是否等于9(即是否达到目的全部打开)因为1代表开灯,0代表关灯,所以只需要遍历整个3*3的数组把值加起来判断是否等于9就可以判断了,如果达成了全开就更新一下最小值。

然后,开关灯的操作也非常简单,非常巧妙。

当当前为1时,用1-当前值=0,达成变换状态目的;

当当前值为0时,用1-当前值=1,达成变换状态目的。

我们再把按按钮封装为一个函数,可以让整体变得非常简单。

另外还有一点要提醒,就是按钮值不能大于9,如果大于9直接return,否则会死循环(虽然说这道题的数据不存在找不到方案的情况。)

AC代码:

#include<iostream>
using namespace std;
int a[5][5],ans=0,mni=10;
void fun(int x,int y)//按灯
{
    //为1时,1-1=0,变换成功
    //为0时,1-0=1,变换成功
    //妙啊~~ovo
    a[x][y]=1-a[x][y];
    a[x+1][y]=1-a[x+1][y];
    a[x][y+1]=1-a[x][y+1];
    a[x-1][y]=1-a[x-1][y];
    a[x][y-1]=1-a[x][y-1];
}
void dfs(int k)//搜索
{
    if(k>mni)return;
    int s=0;//开灯状态的灯数量
    for(int i=1; i<=3; i++)//计算开灯的数量
        for(int j=1; j<=3; j++)
            s+=a[i][j];
    if(s==9){//已达到全开状态,更新最小值
        ans=k-1;
        mni=min(ans,mni);
    }
    for(int i=1; i<=3; i++)
        for(int j=1; j<=3; j++){
            fun(i,j);
            dfs(k+1);
            fun(i,j);//回溯,再按一次等于没按,相当于回溯
        }
    return;
}
int main()
{
    for(int i=1; i<=3; i++)
        for(int j=1; j<=3; j++)
            cin>>a[i][j];
            
    dfs(1);//搜索
    cout<<mni;
    return 0;
}