poj1753 位压缩+bfs

来源:互联网 发布:安装ubuntu并分区 编辑:程序博客网 时间:2024/05/22 17:36

题目大意:有一个4*4的方格,每个方格中放一粒棋子,这个棋子一面是白色,一面是黑色。游戏规则为每次任选16颗中的一颗,把选中的这颗以及它四周的棋子一并反过来,当所有的棋子都是同一个颜色朝上时,游戏就完成了。现在给定一个初始状态,要求输出能够完成游戏所需翻转的最小次数,如果初始状态已经达到要求输出0。如果不可能完成游戏,输出Impossible。

主要思想:

1、如果用一个4*4的数组存储每一种状态,不但存储空间很大,而且在穷举状态时也不方便记录。因为每一颗棋子都只有两种状态,所以可以用二进制0和1表示每一个棋子的状态,则棋盘的状态就可以用一个16位的整数唯一标识。而翻转的操作也可以通过通过位操作来完成。显然当棋盘状态id为0(全白)或65535(全黑)时,游戏结束。

2、对于棋盘的每一个状态,都有十六种操作,首先要判断这十六种操作之后是否有完成的情况,如果没有,则再对这十六种操作的结果分别再进行上述操作,显然这里就要用到队列来存储了。而且在翻转的过程中有可能会回到之前的某种状态,而这种重复的状态是不应该再次入队的,所以维护 Visit[i]数组来判断 id==i 的状态之前是否已经出现过,如果不是才将其入队。如果游戏无法完成,状态必定会形成循环,由于重复状态不会再次入队,所以最后的队列一定会是空队列。

3、由于0^1=1,1^1=0,所以翻转的操作可以通过异或操作来完成,而翻转的位置可以通过移位来确定。

#include<iostream>#include<queue>#include<cstdio>using namespace std;#include<memory.h>struct Node{int state;int step;};bool visit[65536];int change[16] =   //16种状态转换,对应4*4的翻子位置{ 51200,58368,29184,12544,     35968,20032,10016,4880, 2248,1252,626,305, 140,78,39,19};int bfs(int state){int i;memset(visit,false,sizeof(visit));    //标记每一个状态都未访问过queue<Node>q;Node cur,next;cur.state = state;cur.step = 0;q.push(cur);visit[state] = true;while(!q.empty()){cur = q.front();q.pop();if(cur.state == 0 || cur.state == 0xffff)   //65535return cur.step;for(i=0;i<16;i++){next.state = cur.state^change[i];next.step = cur.step + 1;if(visit[next.state])continue;if(next.state == 0 || next.state == 0xffff)   //65535return next.step;visit[next.state] = true;q.push(next);}}return -1;}int main(void){int i,j,state,ans;char ch[5][5];while(scanf("%s",ch[0])!=EOF){for(i = 1 ; i < 4 ; ++i)scanf("%s",ch[i]);state = 0;for(i = 0 ; i < 4 ; ++i){for(j = 0 ; j < 4 ; ++j){state <<= 1;if(ch[i][j] == 'b')state += 1;//state ^= (1<<((3-i)*4+(3-j)));}}ans = bfs(state);if(ans == -1)puts("Impossible");elseprintf("%d\n",ans);}return 0;}


0 0
原创粉丝点击