【状态压缩 开关问题】 费解的开关

传送门

题意

\(25\)盏灯排成一个\(5\times 5\)的方形。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:
和这个灯上下左右相邻的灯也要相应地改变其状态。\(1\)表示灯亮,\(0\)表示灯关
给定\(n\)个初始状态,判断每个状态能否在\(6\)步以内使所有灯变亮

数据范围

$0 < n \leq 500 $

题解

灯状态的改变就是异或运算,每次改变都和1异或即可

  • 枚举第一行的点击方式,采用位运算的方式,枚举\(0\sim 31\)\(32\)\(5\)位二进制数
  • 第一行的点击可以不使第一行全部变成开\(1\),根据性质通过第二行进行操作

每一种第一行判定\(6\)步内能否将矩阵变为\(1\)
性质:

  1. 每个位置最多会被点击一次,操作两次的话,相当于不操作,必然是不满足最优解。
  2. 固定了第一行,则满足题意的点击方案至多只有\(1\)种。
  3. 当第i行某一位为\(0\)时,若前\(i\)行已被固定,只能点击第\(i+1\)行该位置上的数字使其该位置改变。
  4. 点击的先后顺序不影响最终结果

第一行状态确定后结果就确定了,枚举所有第一行情况,求出所有点击方式的最小操作,最后判断一下合法情况的最小值是否在6步内即可

Code

#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<n;i++)
using namespace std;
const int N=10;
char g[N][N];
int _;
int dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0};

void turn (int x,int y)
{
    rep(i,0,5)
    {
        int a=x+dx[i],b=y+dy[i];
        if(a>=0 && a<5 && b>=0 && b<5)
            g[a][b] ^= 1;
    }
}

int get()
{
    int ans=1e9;
    char back[N][N];
    memcpy(back,g,sizeof g);

    rep(i,0,1<<5) //枚举第一行的点击方式
    {
        int res=0;

        rep(j,0,5)
        {
            if(!(i>>j&1))
            {
                res++;
                turn(0,j);
            }
        }

        rep(j,0,4)
            rep(k,0,5)
                if(g[j][k]=='0')
                {
                    res++;
                    turn(j+1,k);
                }
        bool ok=1;
        rep(j,0,5)
            if(g[4][j]=='0')
            {
                ok=0;
                break;
            }
        if(ok)
            ans=min(ans,res);

        memcpy(g,back,sizeof back);
    }
    if(ans>6) ans=-1;

    return ans;
}

int main()
{
    for(scanf("%d",&_);_;_--)
    {
        rep(i,0,5) scanf("%s",g[i]);
        printf("%d\n",get());
    }
    return 0;
}
posted @ 2020-05-27 22:08  Hyx'  阅读(182)  评论(0)    收藏  举报