费解的开关

简介: 费解的开关题解

你玩过“拉灯”游戏吗?

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

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

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

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

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

下面这种状态

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

01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:

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

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

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

分析题意

给一个5行5列的01矩阵, 给的数据每行数字中间没有空格,我们用char数组读会更好

玩法:点击一个位置会使当前位置以及上下左右四个方向的数字变化(0变成1,1 变成0)这里可以使用异或运算

目标:最少点击多少次才能让所有位置上的数变成1;

性质

  1. 要计算最少点击次数,所有每个位置最多只会被点击一次(也可能不点击)
  2. 点击的前后顺序不会影响结果,先点第一行和先点最后一行都一样,我们规定从上往下依次确定每个位置需不需要点击
  3. 当我们点击完第一行时,这个矩阵已经确定

    ​ 什么意思呢,当第一行点击完,我们去点第二行,为了使第一行能全部变成1,我们会在第一行数字为零的 位置的下一行点击,这样不影响第一行其他位置的前提下使当前位置变成1

    ​ 这样第二行需不需要点击也确定了,接下来同样操作直到点完最后一行

    如果点完最后一行,全部位置都变成1,此方法合法,不然就不合法

  4. 所以我们只需要确定第一行到底怎么点,用二进制方法枚举即可

代码

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

using namespace std;

char g[8][8], back[8][8];

int dx[5] = {-1, 0, 1, 0, 0}, dy[5] = {0, 1, 0, -1, 0};


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

int calc()
{
    int ans = 1e9;                          //要找点击次数最少的答案,初始化成大数
    for(int i = 0; i < 1 << 5; i ++ )       //枚举第一行怎么点击
    {
        memcpy(g, back, sizeof g);      //每次开始前都恢复一下g数组
        int res = 0;                    //记录一下操作了几次
        
        for(int j = 0; j < 5; j ++ )        //查看第一行的每一列怎么变化
        {
            if(i >> j & 1)                  //是1就变
            {
                work(0, j); 
                res ++ ;
            }
        }
        
                                        //接下来查看0-4行的状态,看看1-5行每个位置怎么点击
        for(int i = 0; i < 4; i ++ )
        {
            for(int j = 0; j < 5; j ++ )
            {
                if(g[i][j] == '0')          //如果位置为0,点击它的下一行,使它变成1
                {
                    work(i + 1, j);   
                    res ++ ;
                }
            }
        }
        
        bool f = true;
        for(int i = 0; i < 5; i ++ )    //最后查看如果第五行的每一列都是1,就成功
        {
            if(g[4][i] == '0') 
            {
                f = false;
                break;
            }
        }
        
        if(f) ans = min(ans, res);
    }
    
    if(ans > 6) return -1;
    else return ans; 
}

int main()
{
    int t;
    cin >> t;
    while(t -- )
    {
        for(int i = 0; i < 5; i ++ ) cin >> g[i];
        memcpy(back, g, sizeof g);                      //我们会把g数组来回变化
                                                        //为了每次都能回到最初的状态,拷贝一下g数组
        cout << calc() << endl;
    }
    return 0;
}
目录
相关文章
|
调度
MacBookPro外接显示器程序全屏状态,另一个显示器就黑屏
MacBookPro外接显示器程序全屏状态,另一个显示器就黑屏
715 0
MacBookPro外接显示器程序全屏状态,另一个显示器就黑屏
|
算法
费解的开关/翻硬币
费解的开关/翻硬币
128 0
费解的开关笔记
费解的开关笔记
73 0
|
Kubernetes 监控 安全
一个开关就让服务网格变快——实验篇
作为业内首个全托管Istio兼容的阿里云服务网格产品ASM,一开始从架构上就保持了与社区、业界趋势的一致性,控制平面的组件托管在阿里云侧,与数据面侧的用户集群独立。ASM产品是基于社区Istio定制实现的,在托管的控制面侧提供了用于支撑精细化的流量管理和安全管理的组件能力。通过托管模式,解耦了Istio组件与所管理的K8s集群的生命周期管理,使得架构更加灵活,提升了系统的可伸缩性。从2022年4月
一个开关就让服务网格变快——实验篇
枚举时对数组操——三刷AcWing 95. 费解的开关
枚举时对数组操——三刷AcWing 95. 费解的开关
70 0
程序人生 - 汽车后视镜锁车自动折叠为啥失灵?
程序人生 - 汽车后视镜锁车自动折叠为啥失灵?
120 0
程序人生 - 汽车后视镜锁车自动折叠为啥失灵?
|
前端开发
前端工作总结141-根据后台传值动态显示开关状态及文字说明(0为文字,1为图标)
前端工作总结141-根据后台传值动态显示开关状态及文字说明(0为文字,1为图标)
160 0
|
Web App开发 安全 JavaScript
Chrome 92 破坏性功能,我这弹窗有何用?
近期,Chrome 92 进行了发布,我们来看看 Chrome 92 中提及的一个影响比较大的破坏性改动。
Chrome 92 破坏性功能,我这弹窗有何用?