[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

浙公网安备 33010602011771号