费解的开关

费解的开关

https://ac.nowcoder.com/acm/problem/50920

费解的开关

题目描述

你玩过“拉灯”游戏吗?25 盏灯排成一个 5x5 的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态

10111
01101
10111
10000
11011

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

01111
11101
10111
10000
11011

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

01111
11001
11001
10100
11011

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

输入描述

第一行有一个正整数 n,代表数据***有 n 个待解决的游戏初始状态。
以下若干行数据分为 n 组,每组数据有 5 行,每行 5 个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
对于 30%的数据,n≤5;
对于 100%的数据,n≤500。

输出描述

输出数据一共有 n 行,每行有一个小于等于 6 的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若 6 步以内无法使所有灯变亮,请输出“-1”。

输入样例

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

输出样例

3
2
-1

说明

从 0 到 3 的 Hamilton 路径有两条,0-1-2-3 和 0-2-1-3。前者的长度为 2+2+1=5,后者的长度为 1+2+1=4

思路

了解题意不难发现

  • 每个开关至多被按下一次。 // 按下两次没有意义
  • 开关的按下顺序不影响最终结果。
  • 在上一行已经确定的情况下,下一行的点击是确定的。 // 否则没法打开上一行未打开的开关

当点击完倒数第二行时,若最后一行全都为打开的状态,则这样的点击是正确的一组解,所有只需要枚举第一行的点击方法,即可根据上面的三条性质确定每一种方法是否是一组有效解,然后获取最少的点击次数。
对于第一行开关的点击,可用一个整数进行转态压缩,每一位代表一个开关的点击,即遍历 0 ~ (1<<5)-1可枚举所有的点击方法。

示例代码

#include <iostream>
#include <string.h>

const int INF = 0x3F3F3F3F;
const int PRESSNUM = 6;
const int N = 5;
char board[N][N + 1];
char temp[N][N + 1];
int pressNum;

bool inBoard(int i, int j)
{
    return i >= 0 && j >= 0 && i < N && j < N;
}

const int _I[] = {-1, 0, 0, 1, 0};
const int _J[] = {0, -1, 0, 0, 1};

void press(int i, int j)
{
    pressNum++;
    for (int k = 0; k < 5; k++)
    {
        int ii = i + _I[k];
        int jj = j + _J[k];
        if (inBoard(ii, jj))
            temp[ii][jj] = temp[ii][jj] == '0' ? '1' : '0';
    }
}

int solve()
{
    int minp = INF;
    for (int s = 0; s < (1 << N); s++)
    {
        pressNum = 0;
        memcpy(temp, board, sizeof(board));
        for (int i = 0; i < N; i++)
            if (s & (1 << i))
                press(0, i);

        for (int i = 0; i < N - 1; i++)
        {
            for (int j = 0; j < N; j++)
            {
                if (temp[i][j] == '0')
                {
                    press(i + 1, j);
                    if (pressNum > PRESSNUM)
                        goto end_for;
                }
            }
        }
        { // goto不能跳过变量定义
            bool flg = true;
            for (int i = 0; i < N; i++)
                flg = flg && temp[N - 1][i] == '1';
            if (flg && pressNum < minp)
                minp = pressNum;
        }
    end_for:;
    }
    return minp == INF ? -1 : minp;
}

using namespace std;

int main(int argc, char const *argv[])
{
    int n;
    cin >> n;
    for (int k = 0; k < n; k++)
    {
        for (int i = 0; i < N; i++)
        {
            cin >> board[i];
        }
        cout << solve() << endl;
    }

    return 0;
}
全部评论

相关推荐

头像
今天 14:28
长沙理工大学
刷算法真的是提升代码能力最快的方法吗?&nbsp;刷算法真的是提升代码能力最快的方法吗?
牛牛不会牛泪:看你想提升什么,代码能力太宽泛了,是想提升算法能力还是工程能力? 工程能力做项目找实习,算法也分数据结构算法题和深度学习之类算法
点赞 评论 收藏
分享
ProMonkey2024:5个oc?厉害! 但是有一个小问题:谁问你了?😡我的意思是,谁在意?我告诉你,根本没人问你,在我们之中0人问了你,我把所有问你的人都请来 party 了,到场人数是0个人,誰问你了?WHO ASKED?谁问汝矣?誰があなたに聞きましたか?누가 물어봤어?我爬上了珠穆朗玛峰也没找到谁问你了,我刚刚潜入了世界上最大的射电望远镜也没开到那个问你的人的盒,在找到谁问你之前我连癌症的解药都发明了出来,我开了最大距离渲染也没找到谁问你了我活在这个被辐射蹂躏了多年的破碎世界的坟墓里目睹全球核战争把人类文明毁灭也没见到谁问你了(别的帖子偷来的,现学现卖😋)
点赞 评论 收藏
分享
10-30 23:23
已编辑
中山大学 Web前端
去B座二楼砸水泥地:这无论是个人素质还是专业素质都👇拉满了吧
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务