[PKU] 1753 Flip Game [状态压缩,DFS/BFS,枚举]

题目来源:PKU 1753 [Northeastern Europe 2000]

题目大意:有一个4*4的棋盘,每个位置可放黑棋或白棋,给你一个初始摆放状态,经过一定操作,使得最终棋牌为全黑或全白。规则如下:选定任意一枚棋子,连同它四周的棋子(若有的话),都变为其相反的颜色(即黑变白,白变黑),此记为一次有效操作。问:最少经过这样几次操作能够得到最终结果(即全黑或全白)?

简单分析:根据输入要求,b代表黑棋(black),w代表白棋(white)。因为总共才16个位置,且只有黑白两种表示,此时,可对每一次状态进行二进制压缩(其中b代表1,w代表0),例如:

bwwb
bbwb
bwwb
bwww

即可表示为1001 1101 1001 1000,其十进制值为40344。同时,计算可知根据黑白棋的摆放情况,总共有2^16种不同的状态。每一次经过有效的操作后,状态都会发生改变,此时,可借助二进制位运运算实现状态的改变,即对原有状态(相应的十进制表示)进行异或操作,以此来改变其对应二进制数的相关位置的值(1变0,0变1)。

例如:

先假设前一个状态为:

wwww
wwww
wwww
wwww

即二进制表示为0000 0000 0000 0000,十进制对应为0。若此时选定左上角第一个棋子进行操作,根据规则,它右边和下边的也要同时进行变换(因为其左边和上边为空,不做考虑),之后,相应的状态用二进制表示,应变为:1100 1000 0000 0000,十进制值为51200。这个过程相当于对十进制数51200进行对十进制数0的异或操作,即next=0^(51200),而51200这个数则可以根据对十进制数1进行相应的左移操作得到。同时,我们知道,棋牌总共有16个位置,也就是说相应的不同的操作也有16种,即有16个不同的数经过异或操作用来改变前一个状态的值。那么,就先将这16个数枚出来吧,代码如下:

void init()
{
	int p[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
	int i,j,k,temp,x,y;
	for(i=0;i<4;++i)
		for(j=0;j<4;++j) {
			temp=0;
			temp^=(1<<(i*4+j));
			for(k=0;k<4;++k) {
				x=i+p[k][1];
				y=j+p[k][0];
				if(x<0||y<0||x>3||y>3) continue;
				temp^=(1<<(x*4+y));
			}
			printf("%d,",temp);
		}
}

之后,问题就变得简单了,只要依次遍历所有的状态,即可得出相应的结果,当然,要合理的选择遍历的方法。根据下面的代码,像常见的DFS跟BFS都可以AC,但是,我实现的DFS要遍历完所有的状态才能得出结果,并且采用递归版本,所以效率不高,AC时的时间为16MS。BFS为0MS,因为遍历的时候总是在最终结果的到达与未到达之间,即到达最终结果的时候,此时改变的次数也最少。

完整的AC代码如下:

#include<iostream>
using namespace std;

int t[]={19,39,78,140,305,626,1252,2248,4880,10016,20032,35968,12544,29184,58368,51200};
int MIN;


//void DFS(int state,int num,int deep)
//{
//	if(deep>15) {
//		if(state==0||state==65535) {
//			if(num<MIN) MIN=num;
//		}
//		return;
//	}
//	DFS(state^t[deep],num+1,deep+1);
//	DFS(state,num,deep+1);
//}
//
const int SIZE=65535;

int BFS(int state)
{
	int visited[SIZE],d[SIZE],u,v,i;
	int Qu[SIZE],rear,front;
	memset(visited,0,sizeof(visited));
	
	visited[state]=1;
	d[state]=0;
	rear=front=0;
	Qu[++rear]=state;
	while(rear!=front) {
		u=Qu[++front];
		for(i=0;i<16;++i) {
			v=u^t[i];
			if(v==0 || v==65535) return d[u]+1;
			if(visited[v]==0) {
				visited[v]=1;
				d[v]=d[u]+1;
				Qu[++rear]=v;
			}
		}
		visited[u]=-1;
	}
	return -1;
}

int main()
{
	// init();
	char ch[5][5];
	int i,j,start;
	while(scanf("%s",ch[0])!=EOF){
		start=0;MIN=0xfffffff;
		for(i=1;i<4;++i)
			scanf("%s",ch[i]);
		for(i=0;i<4;++i)
			for(j=0;j<4;++j)
				if(ch[i][j]=='b') start^=(1<<((3-i)*4+(3-j)));
		if(start==0||start==65535) printf("0\n");
		else {
		 // DFS(start,0,0);
		 // MIN==0xfffffff? printf("Impossible\n"):printf("%d\n",MIN);
			int result=BFS(start);
			result==-1?printf("Impossible\n"):printf("%d\n",result);
			
		}
	}
	return 0;
}

最后附上(据说是官方的)测试数据:

bwbw
wwww
bbwb
bwwb
Impossible
bwwb
bbwb
bwwb
bwww
4
wwww
wwww
wwww
wwww
0
bbbb
bbbb
bbbb
bbbb
0
bbbb
bwbb
bbbb
bbbb
Impossible
bwbb
bwbb
bwbb
bbbb
Impossible
bwbb
wwwb
bwbb
bbbb
1
wwww
wwwb
wwbb
wwwb
1
wwww
wwww
wwwb
wwbb
1
wbwb
bwbw
wbwb
bwbw
Impossible
bbbb
bwwb
bwwb
bbbb
4
bwwb
wbbw
wbbw
bwwb
4
bbww
bbww
wwbb
wwbb
Impossible
bbwb
bbbw
wwbb
wwwb
Impossible
wwwb
wwbw
wbww
wwbw
Impossible
bbbb
wwww
wwbb
wbbb
Impossible
bwwb
wbwb
wbbb
wbbb
4
bwbb
bwbb
bwbw
bbbw
5
wbwb
bbbb
bbww
wbbb
6
bbwb
bbbb
wbwb
bbbb
5
posted @ 2010-04-01 18:40  timebug  Views(1684)  Comments(0Edit  收藏  举报