poj 3131 Cubic Eight-Puzzle (双向bfs+深度控制 立体八数码)

Cubic Eight-Puzzle
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 1344   Accepted: 451

Description

Let’s play a puzzle using eight cubes placed on a 3 × 3 board leaving one empty square.

Faces of cubes are painted with three colors. As a puzzle step, you can roll one of the cubes to a adjacent empty square. Your goal is to make the specified color pattern visible from above by a number of such steps.

The rules of this puzzle are as follows.

  1. Coloring of Cubes: All the cubes area colored in the same way as shown in Figure 1. The opposite faces have the same color.

    poj 3131 Cubic Eight-Puzzle (双向bfs+深度控制 立体八数码)_第1张图片

    Figure 1: Coloring of a cube

  2. Initial Board State: Eight cubes are placed on the 3 × 3 board leaving one empty square. All the cubes have the same orientation as shown in Figure 2. As shown in the figure, squares on the board are given xand y coordinates, (1, 1), (1, 2), …, and (3, 3). The position of the initially empty square may vary.

    poj 3131 Cubic Eight-Puzzle (双向bfs+深度控制 立体八数码)_第2张图片

    Figure 2: Initial board state

  3. Rolling Cubes: At each step, we can choose one of the cubes adjacent to the empty square and roll it into the empty square, leaving the original position empty. Figure 3 shows an example.

    poj 3131 Cubic Eight-Puzzle (双向bfs+深度控制 立体八数码)_第3张图片

    Figure 3: Rolling a cube

  4. Goal: The goal of this puzzle is to arrange the cubes so that their top faces form the specified color pattern by a number of cube rolling steps described above.

Your task is to write a program that finds the minimum number of steps required to make the specified color pattern from the given initial state.

Input

The input is a sequence of datasets. The end of the input is indicated by a line containing two zeros separated by a space. The number of datasets is less than 16. Each dataset is formatted as follows.

x y  
F11 F21 F31
F12 F22 F32
F13 F23 F33

The first line contains two integers x and y separated by a space, indicating the position (xy) of the initially empty square. The values of x and y are 1, 2, or 3.

The following three lines specify the color pattern to make. Each line contains three characters F1jF2j, and F3j, separated by a space. Character Fij indicates the top color of the cube, if any, at the position (ij) as follows:

B: Blue,

W: White,

R: Red,

E: the square is Empty.

There is exactly one ‘E’ character in each dataset.

Output

For each dataset, output the minimum number of steps to achieve the goal, when the goal can be reached within 30 steps. Otherwise, output “-1” for the dataset.

Sample Input

1 2 
W W W 
E W W 
W W W 
2 1 
R B W 
R W W 
E W W 
3 3 
W B W 
B R E 
R B R 
3 3 
B W R 
B W R 
B E R 
2 1 
B B B 
B R B 
B R E 
1 1 
R R R 
W W W 
R R E 
2 1 
R R R 
B W B 
R R E 
3 2 
R R R 
W E W 
R R R
0 0

Sample Output

0 
3 
13 
23 
29 
30 
-1 
-1
 
 
题意:这是一个立体的8数码问题,不过有点区别,有8个立体的正方体摆在3*3的区域内,留一个空格方便移动。移动的规则是:空格旁边的小正方体可以滚动到空格位置,小正方体原来的位置变成空格。每个小正方体6个面有3中颜色,对面颜色相同,分别是white,blue,red。初始状态每个小正方体的摆放方式都一样,从正面看上面是white,前面是red,右面是blue,空格位置给定。现在给定一个初始的空格位置以及一个终态的上表面的颜色分布,求是否能在30步内从初态到终态,能,输出最少步数,不能,输出-1,注意给定的终态只是上表面的颜色分布,其他面上的颜色不做要求。

思路:bfs。这题状态数非常之多,时间空间都要求很高,如果单向bfs,时空复杂度都太高,因此选择双向bfs。首先要解决的是判重问题,这题不像二维平面的8数码,每个位置只有一个状态,因此可以用康托判重,这题每个位置的小方块有6个状态,加上空格,每个位置有7个状态,那么总的状态数就是7^9,显然还是太大了。再考虑到9个位置只能有一个位置是空格,可以把空格单独拿出来判重,这样空间复杂度就降到了6^8*9,勉强可以接受。
这就需要用六进制数表示状态,然后判重也好判了,最后的问题就是模拟方块的滚动了,4个方向滚动,状态会发生怎样的变化都要在草稿纸上画清楚。
ps:这题还要说明一点 因为初状态只有一种 末状态却有2^8种 所以从前往后搜和从后往前搜搜的深度得不一样才行 从前往后多搜点 才能保证双搜沿棱形展开 我是这样控制的 从前往后:21层 从后往前:9层。
 
 
感想:这题程序代码还是蛮长的 所以要思路清晰 想好了 准备好了再下手 不然debug会很不好搞的 
 
代码:(我的程序用的STL 也可以改为自己的队列的 会快一点点 方便看 我就只贴STL代码好了 )
// Memory 31352 KB    Time 2250 ms  
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define maxn 1679616    // 6^8
using namespace std;

int n,m,x,y,ans,dfspos;
int six[]= {1,6,36,216,1296,7776,46656,279936};
int state[3][3];            //  根据前上推状态
int roll[6][4]=             // 状态滚动后的状态
{
    2,2,5,5,
    4,4,3,3,
    0,0,4,4,
    5,5,1,1,
    1,1,2,2,
    3,3,0,0
};
bool vis1[9][maxn];
bool vis2[9][maxn];
int mp[9];
int dx[]= {-1,1,0,0};
int dy[]= {0,0,-1,1};
int undir[]= {1,0,3,2};
int dight[8];
struct Node
{
    int pos;
    int sta;
    int step;
} cur,now;
queue<Node>q1,q2;

int getdight(int xx,int k)         // 开始是按十进制提取的  想错了  应该是提取六进制
{
    int i,j,t;
    if(k==0) t=xx%6;
    else
    {
        t=1;
        for(i=1; i<=k; i++)
            t*=6;
        t=(xx/t)%6;
    }
    return t;
}
bool bfs(int k)
{
    int i,j,mi,cnt1,cnt2,npos,nsta,nx,ny,temp,nstep,flag;
    int tx,ty,tpos,tsta,tstadt;
    memset(vis1,0,sizeof(vis1));
    while(!q1.empty()) q1.pop();
    cur.pos=k;
    cur.sta=0;
    vis1[k][0]=1;
    cur.step=0;
    q1.push(cur);
    cnt1=0;
    cnt2=0;
    while(1)
    {
        flag=0;
        while(q1.front().step<=cnt1&&!q1.empty())
        {
            flag=1;
            now=q1.front();
            q1.pop();
            npos=now.pos;
            nsta=now.sta;
            nstep=now.step;
            nx=npos/3;
            ny=npos%3;
            if(vis2[npos][nsta])
            {
                ans=nstep+cnt2;
                return true ;
            }
//            printf("pos:%d step:%d sta:%d nx:%d ny:%d\n",npos,nstep,nsta,nx,ny);
            for(i=0; i<8; i++)
            {
                dight[i]=getdight(nsta,i);
            }
            for(i=0; i<4; i++)
            {
                tx=nx+dx[i];
                ty=ny+dy[i];
                if(tx>=0&&tx<3&&ty>=0&&ty<3&&nstep<21)
                {
//                    printf("tx:%d ty:%d\n",tx,ty);
                    tpos=tx*3+ty;
                    if(i==2||i==3)           // 左右滚动不会改变相对位置大小 可直接处理
                    {
                        mi=npos>tpos?tpos:npos;
                        tstadt=roll[dight[mi]][undir[i]];
                        tsta=nsta-six[mi]*dight[mi]+six[mi]*tstadt;
                        if(!vis1[tpos][tsta])
                        {
                            vis1[tpos][tsta]=1;
                            cur.step=nstep+1;
                            cur.pos=tpos;
                            cur.sta=tsta;
                            q1.push(cur);
                        }
                    }
                    else if(i==0)            // 向下滚动  要影响三个数的值
                    {
                        tstadt=roll[dight[tpos]][undir[i]];
                        tsta=nsta;
                        for(j=tpos; j<npos; j++)
                        {
                            tsta-=six[j]*dight[j];
                        }
                        tsta+=tstadt*six[npos-1];
                        for(j=tpos+1; j<npos; j++)
                        {
                            tsta+=six[j-1]*dight[j];
                        }
                        if(!vis1[tpos][tsta])
                        {
                            vis1[tpos][tsta]=1;
                            cur.step=nstep+1;
                            cur.pos=tpos;
                            cur.sta=tsta;
                            q1.push(cur);
                        }
                    }
                    else if(i==1)            // 向上滚动  也要影响三个数的值
                    {
                        tstadt=roll[dight[tpos-1]][undir[i]];
                        tsta=nsta;
                        for(j=npos; j<tpos; j++)
                        {
                            tsta-=six[j]*dight[j];
                        }
                        tsta+=tstadt*six[npos];
                        for(j=npos; j<tpos-1; j++)
                        {
                            tsta+=six[j+1]*dight[j];          // 开始就错在这里了  j忘记+1了
                        }
                        if(!vis1[tpos][tsta])
                        {
                            vis1[tpos][tsta]=1;
                            cur.step=nstep+1;
                            cur.pos=tpos;
                            cur.sta=tsta;
                            q1.push(cur);
                        }
                    }
                }
            }
        }
        if(cnt1<21) cnt1++;                  // 设置深度 从前往后不超过21层
        while(q2.front().step<=cnt2&&!q2.empty())
        {
            flag=1;
            now=q2.front();
            q2.pop();
            npos=now.pos;
            nsta=now.sta;
            nstep=now.step;
            nx=npos/3;
            ny=npos%3;
            if(vis1[npos][nsta])
            {
                ans=cnt1+nstep;
                return true ;
            }
//            printf("pos:%d step:%d sta:%d nx:%d ny:%d\n",npos,nstep,nsta,nx,ny);
            for(i=0; i<8; i++)
            {
                dight[i]=getdight(nsta,i);
            }
            for(i=0; i<4; i++)
            {
                tx=nx+dx[i];
                ty=ny+dy[i];
                if(tx>=0&&tx<3&&ty>=0&&ty<3&&nstep<9)
                {
//                    printf("tx:%d ty:%d\n",tx,ty);
                    tpos=tx*3+ty;
                    if(i==2||i==3)           // 左右滚动不会改变相对位置大小 可直接处理
                    {
                        mi=npos>tpos?tpos:npos;
                        tstadt=roll[dight[mi]][undir[i]];
                        tsta=nsta-six[mi]*dight[mi]+six[mi]*tstadt;
                        if(!vis2[tpos][tsta])
                        {
                            vis2[tpos][tsta]=1;
                            cur.step=nstep+1;
                            cur.pos=tpos;
                            cur.sta=tsta;
                            q2.push(cur);
                        }
                    }
                    else if(i==0)            // 向下滚动  要影响三个数的值
                    {
                        tstadt=roll[dight[tpos]][undir[i]];
                        tsta=nsta;
                        for(j=tpos; j<npos; j++)
                        {
                            tsta-=six[j]*dight[j];
                        }
                        tsta+=tstadt*six[npos-1];
                        for(j=tpos+1; j<npos; j++)
                        {
                            tsta+=six[j-1]*dight[j];
                        }
                        if(!vis2[tpos][tsta])
                        {
                            vis2[tpos][tsta]=1;
                            cur.step=nstep+1;
                            cur.pos=tpos;
                            cur.sta=tsta;
                            q2.push(cur);
                        }
                    }
                    else if(i==1)            // 向上滚动  也要影响三个数的值
                    {
                        tstadt=roll[dight[tpos-1]][undir[i]];
                        tsta=nsta;
                        for(j=npos; j<tpos; j++)
                        {
                            tsta-=six[j]*dight[j];
                        }
                        tsta+=tstadt*six[npos];
                        for(j=npos; j<tpos-1; j++)
                        {
                            tsta+=six[j+1]*dight[j];
                        }
                        if(!vis2[tpos][tsta])
                        {
                            vis2[tpos][tsta]=1;
                            cur.step=nstep+1;
                            cur.pos=tpos;
                            cur.sta=tsta;
                            q2.push(cur);
                        }
                    }
                }
            }
        }
        if(cnt2<9) cnt2++;                 // 从后往前不超过9步
        if(!flag) return false ;
    }
}
void dfs(int kk,int ssta,int tt)           // 访问到第几个数  状态  已经加进来了几个数
{
    int i,j,temp;
    if(kk>8)
    {
        if(!vis2[dfspos][ssta])                  // 这里的dfspos得用全局变量
        {
            vis2[dfspos][ssta]=1;
            cur.step=0;
            cur.sta=ssta;
//            printf("pos:%d ssta:%d\n",dfspos,ssta);
            q2.push(cur);
        }
        return ;
    }
    if(mp[kk]==-1)
    {
        dfspos=kk;
        dfs(kk+1,ssta,tt);
    }
    else
    {
        temp=mp[kk]*six[tt];
        dfs(kk+1,ssta+temp,tt+1);
        temp=(mp[kk]+1)*six[tt];
        dfs(kk+1,ssta+temp,tt+1);
    }
}
int main()
{
    int i,j,t,tst,tpos,temp;
    char s[3];
    state[0][1]=0;
    state[0][2]=1;
    state[1][0]=2;
    state[1][2]=3;
    state[2][0]=4;
    state[2][1]=5;
    while(scanf("%d%d",&x,&y),x||y)
    {
        x--;
        y--;
        t=y*3+x;
        for(i=0; i<3; i++)
        {
            for(j=0; j<3; j++)
            {
                scanf("%s",s);
                temp=i*3+j;
                if(s[0]=='W') mp[temp]=0;
                else if(s[0]=='R') mp[temp]=2;
                else if(s[0]=='B') mp[temp]=4;
                else
                {
                    mp[temp]=-1;
                    tpos=i*3+j;
                }
            }
        }
        cur.pos=tpos;
        memset(vis2,0,sizeof(vis2));
        while(!q2.empty()) q2.pop();
        dfs(0,0,0);                    // 将终态进队列
        if(bfs(t)) printf("%d\n",ans);
        else printf("-1\n");
    }
    return 0;
}

你可能感兴趣的:(poj 3131 Cubic Eight-Puzzle (双向bfs+深度控制 立体八数码))