【位运算或高斯消元】开关游戏
Time Limit:10000MS Memory Limit:65536K
Total Submit:98 Accepted:39
Case Time Limit:1000MS
Description
在4x4的游戏棋盘上有16个方格,每个方格是一个开关,按一下点亮当前方格,再按以下就关闭当前方格。但是,当你按下当前方格时,它的上下左右四个方格同时会受到影响,原来点亮的会被关闭,原来关闭的会被点亮。
游戏的目标是使得16个格子全部点亮或者全部熄灭,你的任务是找出最少的操作步数字。
Input
一个4x4的矩阵,"w"表示处于点亮状态,"b"表示处于关闭状态
Output
一个整数,表示最小步数,如果无解,输出“Impossible”
Sample Input
bwwb
bbwb
bwwb
bwww
Sample Output
4
Source
Northeastern Europe 2000
此题可以利用位运算进行状态压缩
假设开始时棋盘的状态如下:(分别用0,1表示关闭,开启)
0 1 0 1
1 1 1 1
0 0 1 0
0 1 1 0
从上到下,从左到右将数字排列成:0101 1111 0010 0110
则101111100100110为一个二进制数,转化为十进制数为24358
则所有状态为0000000000000000~1111111111111111,即从0~65535表示每一个状态
想到从mark[65535]记录当前状态是否讨论过
并且要用宽搜搜索每一个状态
在按按钮的时候,即将第k位取反,可用以下代码实现:
int work(int s,int k){
int now=s;
now^=1<<k;
if(k>3)now^=1<<(k-4);
if(k<12)now^=1<<(k+4);
if(k%4!=0)now^=1<<(k-1);
if((k+1)%4!=0)now^=1<<(k+1);
return now;
}
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
int v[16]={32768,16384,8192,4096,2048,1024,512,256,128,64,32,16,8,4,2,1};
bool mark[68000];
long long step[68000];
int start;
queue<int>q;
int work(int s,int k){
int now=s;
now^=1<<k;
if(k>3)now^=1<<(k-4);
if(k<12)now^=1<<(k+4);
if(k%4!=0)now^=1<<(k-1);
if((k+1)%4!=0)now^=1<<(k+1);
return now;
}
long long bfs(){
int i,now,temp;
q.push(start);
mark[start]=true;
while(!q.empty()){
temp=q.front();
q.pop();
for(i=0;i<16;i++){
now=work(temp,i);
if(!mark[now]){
if(now==0||now==65535)return step[temp]+1;
mark[now]=true;
q.push(now);
step[now]=step[temp]+1;
}
}
}
return -1;
}
int main(){
char ch;
int i=0;
while(i<16){
ch=getchar();
if(ch=='b'||ch=='w'){
if(ch=='w')start+=v[i];
i++;
}
}
if(start==0||start==65535){printf("0");return 0;}
long long ans=bfs();
if(ans==-1)printf("Impossible");
else printf("%d",ans);
}