【状态压缩 开关问题】 费解的开关
传送门
题意
\(25\)盏灯排成一个\(5\times 5\)的方形。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:
和这个灯上下左右相邻的灯也要相应地改变其状态。\(1\)表示灯亮,\(0\)表示灯关
给定\(n\)个初始状态,判断每个状态能否在\(6\)步以内使所有灯变亮
数据范围
$0 < n \leq 500 $
题解
灯状态的改变就是异或运算,每次改变都和1异或即可
- 枚举第一行的点击方式,采用位运算的方式,枚举\(0\sim 31\)这\(32\)个\(5\)位二进制数
- 第一行的点击可以不使第一行全部变成开\(1\),根据性质通过第二行进行操作
每一种第一行判定\(6\)步内能否将矩阵变为\(1\)
性质:
- 每个位置最多会被点击一次,操作两次的话,相当于不操作,必然是不满足最优解。
- 固定了第一行,则满足题意的点击方案至多只有\(1\)种。
- 当第i行某一位为\(0\)时,若前\(i\)行已被固定,只能点击第\(i+1\)行该位置上的数字使其该位置改变。
- 点击的先后顺序不影响最终结果
第一行状态确定后结果就确定了,枚举所有第一行情况,求出所有点击方式的最小操作,最后判断一下合法情况的最小值是否在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;
}

浙公网安备 33010602011771号