poj 1753 Flip Game 枚举(bfs+状态压缩)

题目: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);
}

  

原文地址:https://www.cnblogs.com/bfshm/p/3147364.html