题目:http://poj.org/problem?id=1753
因为粗心错了好多次……,尤其是把1<<15当成了65535;
参考博客:http://www.cnblogs.com/kuangbin/archive/2011/07/30/2121677.html
主要思想:
1.如果用一个4*4的数组存储每一种状态,不但存储空间很大,而且在穷举状态时也不方便记录。因为每一颗棋子都只有两种状态,所以可以用二进制0和1表示每一个棋子的状态,则棋盘的状态就可以用一个16位的整数唯一标识。而翻转的操作也可以通过通过位操作来完成。显然当棋盘状态id为0(全白)或65535(全黑)时,游戏结束。
2.对于棋盘的每一个状态,都有十六种操作,首先要判断这十六种操作之后是否有完成的情况,如果没有,则再对这十六种操作的结果分别再进行上述操作,显然这里就要用到队列来存储了。而且在翻转的过程中有可能会回到之前的某种状态,而这种重复的状态是不应该再次入队的,所以维护isVis[i]数组来判断id==i的状态之前是否已经出现过,如果不是才将其入队。如果游戏无法完成,状态必定会形成循环,由于重复状态不会再次入队,所以最后的队列一定会是空队列。
3.由于0^1=1,1^1=0,所以翻转的操作可以通过异或操作来完成,而翻转的位置可以通过移位来确定。
#include<stdio.h> #include<string.h> #include<queue> using namespace std; int id,vis[1<<17]; struct node { int x,step; }; int bfs() { queue<node>q; int i; struct node pos,cur; if(id==0||id==65535) return 0; memset(vis,0,sizeof(vis)); pos.step=0; pos.x=id; q.push(pos); vis[id]=1; while(!q.empty()) { cur=q.front(); q.pop(); for(i=0; i<=15; i++) { pos.x=(cur.x^(1<<i)); if(i+4<16) pos.x^=(1<<(i+4)); if(i-4>=0) pos.x^=(1<<(i-4)); if(i%4!=0) pos.x^=(1<<(i-1)); if((i+1)%4!=0) pos.x^=(1<<(i+1)); if(vis[pos.x]==0) { vis[pos.x]=1; pos.step=cur.step+1; q.push(pos); } if(pos.x==0||pos.x==65535) return pos.step; } } return -1; }; int main() { int i,f,j; char c; id=0; for(i=0; i<4; i++) { for(j=0; j<4; j++) { id=(id<<1); scanf("%c",&c); if(c=='b') id+=1; else id+=0; } getchar(); } f=bfs(); if(f==-1) printf("Impossible "); else printf("%d ",f); }