想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
转载请注明出处:http://blog.csdn.net/wangjian8006
题目大意:给一个4×4的期盘,b代表白色,w代表黑色,当翻一个棋子,那么这个棋子的上,下,左,右都会跟着翻过来
问翻最少的次数能够使棋盘达到全部是黑色或者全部是白色,如果不可能输出
解决思路:广搜+位运算。
因为棋子的状态只有白色和黑色,相当于二进制的0和1,所以可以用位运算。
我们用w表示1,b表示0,棋盘有16个方块,代表16位
那么用一个flag,又可以作为标记数组,代表棋盘状态是否被搜过,如果被搜过之后就不用再往后面搜,左上
角为最高位,右下为最低位,那么他们最大为0xFFFF(全部为1),而0xFFFF=65535,所以开一个标记数组不是很大
而flag=0代表没被找过,flag>0代表棋盘找到flag的时候所翻的最少次数。如果广搜之后找得到0x0或0xFFFF就返回
现在广搜的层数
而广搜在每一个节点,有16个点可以翻,所以它的周围有16个节点,那么翻第一个是什么情况,翻第二个是什么情况
一直到翻十六个是什么情况,其实就是说翻某一个点的时候,有哪些点要变,哪些点不变,这个时候打一个表,filp
代表每一个棋盘和flip取异或就可以了
/*
Memory 428K
Time 16MS
*/
#include <queue>
#include <iostream>
using namespace std;
int t;
int flag[70000];
int flip[16]={0xC800,0xE400,0x7200,0x3100,0x8C80,0x4E40,0x2720,0x1310,0x08C8,0x04E4,0x0272,0x0131,
0x008C,0x004E,0x0027,0x0013};
void bfs(){
int v,i;
queue<int> q;
flag[t]=1;
q.push(t);
while(!q.empty()){
v=q.front();
q.pop();
for(i=0;i<16;i++){
t=v^flip[i];
if(t==0x0 || t==0xFFFF) {flag[t]=flag[v]+1;return ;}
if(!flag[t]){
flag[t]=flag[v]+1;
q.push(t);
}
}
}
}
int main(){
int i,j;
char s[5];
while(gets(s)){
t=0;
for(i=0;i<4;i++)
if(s[i]=='w') t=(t<<1)+1;
else t=t<<1;
for(j=0;j<3;j++){
gets(s);
for(i=0;i<4;i++)
if(s[i]=='w') t=(t<<1)+1;
else t=t<<1;
}
memset(flag,0,sizeof(flag));
if(t==0x0 || t==0xFFFF) {printf("0\n");continue;}
else bfs();
if(flag[0xFFFF] || flag[0x0]) printf("%d\n",flag[t]-1);
else printf("Impossible\n");
}
return 0;
}