[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

你可能感兴趣的:(game)