poj 1753(位压缩+搜索)

来源:互联网 发布:java开发平台搭建 编辑:程序博客网 时间:2024/06/05 03:31

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

      分析:看完这道题,一种很直观的想法就是,对给出的初始状态,由于能进行16种不同的操作(总共16枚棋子),因此就枚举每一种操作后能达到的状态,看是否满足题目要求,若不能就继续进行操作,直到满足题目条件或判断出无法完成为止。

      刚开始本以为这样会超时的,毕竟每一个状态都能衍生出16个状态,这样就相当于一个16叉树了。但在网上看了大神的题解后才知道,实际上暴力搜索是能求解的。。。只是如果每次都用一个4*4的数组存储状态,不仅空间开不下,而且处理和记录状态信息也很麻烦,因此要用二进制压缩状态。

      1:由于每个棋子都是黑色或白色的,因此,可以用0表示白色,1表示黑色,这样每个棋子就只能处于两种状态之一,而整个棋盘可以用一个16位整数唯一标示。显然,当棋盘表示0(全白)或65535(全黑)时,游戏结束。

      2:翻转某个棋子等价于::让表示它的数字异或1,如0^1=1,1^1=0。这样,对棋盘的每次操作就转换为:对标示它的数字按位异或1。例子见代码注释;

      3:对每个状态衍生出的16种状态,首先判断其中有没有全黑或全白的,若没有就继续进行衍生。这里很明显用队列存储状态。衍生出的状态依次入队,但已经出现过的状态不应再次入队,因此要用一个标志数组vis[]对出现的状态进行标示。这样,无论是否有解,队列最终都会为空。

/*用0表示白色,1表示黑色,每一个状态都有一个16位数唯一标示:eg:    bwwb    bbwb    bwwb    bwww即可表示为1001 1101 1001 1000,其十进制值为40344。对棋盘的每次操作都可以用异或实现。如翻转左上角第一个棋子的话,过程如下:    1001 1101 1001 1000 ^ 1100 1000 0000 0000 = 0101 0101 1001 1000 (翻转后的状态)    翻转第二行第二个棋子的话即为:    1001 1101 1001 1000 ^ 0100 1110 0100 0000 = 1101 0011 1101 1000 (翻转后的状态)    ......    可以看出,翻转左上角第一个棋子时异或的 1100 1000 0000 0000 是由 0000 0000 0000 0000 翻转对应位置所得来的。               翻转第二行第二个棋子时异或的 0100 1110 0100 0000 是由 0000 0000 0000 0000 翻转对应位置所得来的。               .......    因此,总共有16个数,每个数都代表一个操作。当要执行某个操作时,直接异或对应的数就行了。那么程序中首先就先把这16个数求出来。*/#include<cstdio>#include<cstring>#include<iostream>#include<cstdlib>#include<cmath>#include<algorithm>#include<queue>#include<stack>#include<set>#include<map>using namespace std;typedef pair<int,int> pii;const int M=1010;const int N=100010;int n,m,change[16];int d[4][2]={0,1,0,-1,1,0,-1,0};char s[M][M];bool vis[N];struct node{    int d,step;};void init() // 求出代表操作的16个数,存储在数组change[]里。{    int e=0;    for (int i=0;i<4;i++){        for (int j=0;j<4;j++){            int t=0;            t^=(1<<((3-i)*4+3-j));            for (int k=0;k<4;k++){                int x=i+d[k][0];                int y=j+d[k][1];                if (x>=0 && x<4 && y>=0 && y<4) {                    t^=(1<<((3-x)*4+3-y));                }            }            change[e++]=t; //cout<<t<<endl;        }    }}int get()  // 得到初始棋盘表示的数{    int t=0;    for (int i=0;i<4;i++)        for (int j=0;j<4;j++)        {            t<<=1;            if (s[i][j]=='b')               t|=1;        }    return t;}int bfs(int temp){    int i,j;    node t;    memset(vis,false,sizeof(vis));    t.d=temp; t.step=0;    queue<node> q;    q.push(t);    vis[temp]=true;    while(!q.empty())    {        t=q.front(); q.pop();        if (t.d==0 || t.d==0xffff) return t.step ;        for (i=0;i<16;i++) {             node r;             r.d=t.d^change[i];             r.step=t.step+1;             if (vis[r.d]) continue;             vis[r.d]=true;             if (r.d==0 || r.d==0xffff)  return r.step;             q.push(r);        }    }    return -1;}int main(){    int i,j,ans;    init();    while(~scanf("%s",s[0]))    {        for (i=1;i<4;i++) scanf("%s",s[i]);        int t=get();        if (t==0 || t==0xffff) ans=0;        else ans=bfs(t);        if (ans==-1) puts("Impossible");        else printf("%d\n",ans);    }}


0 0