《算法竞赛进阶指南》-AcWing-95. 费解的开关-题解

本文介绍了如何使用C++解决一个关于5x5灯泡地图的问题,其中每次点击一盏灯会改变其及周围灯的状态。目标是在不超过6步内将所有灯点亮。通过枚举第一行灯的状态并应用剪枝策略来优化搜索过程,最终找到解决方案或输出无法完成任务的步数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

费解的开关

传送门

问题描述

5 × 5 5\times5 5×5 01 01 01地图, 0 0 0代表这个灯是关着的, 1 1 1代表是开着的。

每次点击一盏灯,它及它的上下左右共 5 5 5盏灯的开关状态都会发生置换。

问你能不能在 6 6 6步之内把所有的灯点亮。

样例输入

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

样例输出

3
2
-1

解题思路

不如先枚举第一行的每个灯的点与不点( 2 5 = 32 2^5=32 25=32种情况),之后第一行就不再动了,第一行没亮的灯由第二行对应的那盏灯点亮。

最后看最后一行是否都是亮着的。

剪枝:中途一旦遇到步数已经大于6就退出。

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
#define size 5
bool mp[size][size]; // 这个用来保存输入
bool test[size][size]; // 这个是用来尝试的数组,每次copy自mp,之后随便改
void debug(bool mp[size][size]) // debug用,打印当前数组
{
    for(int i=0;i<size;i++)
    {
        for(int j=0;j<size;j++)
            cout<<mp[i][j];
        puts("");
    }
}
void scan() // 输入用
{
    for (int i = 0; i < size; i++)
    {
        string s;
        cin>>s;
        for(int j=0;j<size;j++)
            mp[i][j]=s[j]=='1';
    }
}
int dir[5][2]={{-1,0},{1,0},{0,-1},{0,1},{0,0}}; // 这个点开始,上下左右中,共影响5个点
void click(int x, int y)
{
    // puts("Before click:");
    // debug(test);
    for(int i=0;i<5;i++) // 遍历这5个点
    {
        int tx=x+dir[i][0]; // toX:要到的x是当前的x加上步长
        int ty=y+dir[i][1];
        if(tx>=0&&tx<size&&ty>=0&&ty<size) // 如果没有出界
            test[tx][ty]^=1; // 开变成关,关变成开
            // test[tx][ty]=~test[tx][ty]; // 不可以!
    }
    // printf("After clicked (%d, %d):\n",x,y);
    // debug(test);
}
void copy() // 把mp赋值给test
{
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
            test[i][j]=mp[i][j];
}
int execute(int step) // 执行函数
{
    // debug(test);//*******
    for(int i=1;i<size;i++) // 第2~5行
    {
        for(int j=0;j<size;j++) // 第1~5个
        {
            if(!test[i-1][j]) // 如果是关着的
            {
                click(i,j); // 点击
                step++; // 步数+1
                if(step>6)return step; // 剪枝
            }
        }
    }
    // debug(test);//*******
    for(int j=0;j<size;j++) // 遍历最后一行的每一盏灯
        if(!test[size-1][j]) // 如果是关着的
            return 7; // 不可以
    return step;
}
int main()
{
    int N;
    cin >> N;
    while (N--)
    {
        scan(); // 读入
        int m_step=7; // 最小步数(7已经代表不行了)
        for(int i=0;i<1<<size;i++) // 枚举第一行的所有状态
        {
            copy(); // 复制给test
            int step=0;
            for(int j=0;j<size;j++) // 遍历这个状态数的每一位
                if(i>>j&1) // 如果这一位是1
                    click(0,j),step++; // 点击这个点(其实j反了也无所谓,因为枚举了每一种状态)
            step=execute(step); // 执行
            m_step=min(m_step,step); // 取最优
        }
        cout<<(m_step<=6?m_step:-1)<<endl; // 输出
    }
    return 0;
}

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/119274660

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Tisfy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值