NKOI 2222 开关游戏

【位运算或高斯消元】开关游戏

Time Limit:10000MS  Memory Limit:65536K
Total Submit:98 Accepted:39 
Case Time Limit:1000MS

Description

在4x4的游戏棋盘上有16个方格,每个方格是一个开关,按一下点亮当前方格,再按以下就关闭当前方格。但是,当你按下当前方格时,它的上下左右四个方格同时会受到影响,原来点亮的会被关闭,原来关闭的会被点亮。 
NKOI 2222 开关游戏_第1张图片 

游戏的目标是使得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);
}

你可能感兴趣的:(NKOI 2222 开关游戏)