poj3131Cubic Eight-Puzzle(立体8数码,双向bfs+6进制压缩)

Cubic Eight-Puzzle
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 1301 Accepted: 434

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.

    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 x andy coordinates, (1, 1), (1, 2), …, and (3, 3). The position of the initially empty square may vary.

    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.

    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.

xy 
F11F21F31
F12F22F32
F13F23F33

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

Source


搞这题又花了一天。。。Orz男神。。。
题目大意:这是一个例题的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,勉强可以接受。队列的话,自己手写2个循环队列,也会节约不少内存(最终经测试这题用循环队列空间开100005就够了)。
接下来考虑状态的表示。每个小正方体有6个状态(想一想,为什么),那么我们如何表示这6种状态呢。这题的空间只给了65535kb,判重用的数组就要花掉将近一半,还要留给2个队列的开销,所以每个搜索的节点空间必须要节省。如果用数组将每个状态存起来,内存显然不够,判断也会很慢。再分析一下可以发现,9个位置除去一个空格,其余8个位置每个位置的状态都是0-5,也就是说总的状态就是从00000000-55555555一共6^8个状态,我们可以将这8个位置的状态压缩成一个6进制整数,这样就很容易判重了,每次移动一下只改变最多3个位置的数,具体过程还是在草稿纸上好好划一下,千万不要出错,不容易调试的。
注意这题初态只有一个,即所有的小方块摆放都是一致的上白前红右蓝,终态只考虑上表面,所以终态不是唯一的,有2^8=256个终态。
写完后用G++提交竟然TLE了,用C++提交才擦着线过。后来发现是因为双向搜的步数问题。一般我们都是正反搜一半,但是这题不一样,正向一开始只有一个状态,反向一开始就有256个状态,那么扩展的时候就会造成正反向不平均,如果还是正反都搜一半的话,就会导致效率下降。所以我们考虑让正向多搜几步,减轻反向搜索的负担。提交十几次后发现当正向搜21步,反向搜9步的时候时空效率是最高的。
详情请见代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
const int d1 = 21;
const int d2 = 9;
const int M = 100005;
const int N = 1679616;//6^8
bool flag[2][N][9];//3690kb
int fac[] = {1,6,36,216,1296,7776,46656,279936};

//6个状态:
//状态号   上   前  右
//0        W    R    B
//1        W    B    R
//2        R    W    B
//3        R    B    W
//4        B    W    R
//5        B    R    W
int roll[6][2] = {{2,5},{4,3},{0,4},{5,1},{1,2},{3,0}};
//6个状态分别往上下左右4个方向转一步到达的新状态

struct node
{
    int state;
    short int pos,step;
}ss,now;

struct que
{
    struct node t[M];
    int head,tail;
    void init()
    {
        head = tail;
    }
    bool empty()
    {
        return head == tail;
    }
    struct node top()
    {
        return t[head];
    }
    void pop()
    {
        head ++;
        if(head == M)
            head = 0;
    }
    void push(struct node a)
    {
        t[tail] = a;
        tail ++;
        if(tail == M)
            tail = 0;
        if(tail + 1 == head)
            printf("queue full\n");
    }
}q[2];
int x,y;
int endstate[10];
int endpos;

void dfs(int cur,int sum,int dp)
{
    if(cur == -1)
    {
        ss.state = sum;
        ss.pos = endpos;
        ss.step = 0;
        q[1].push(ss);
        flag[1][ss.state][ss.pos] = 1;
        return;
    }
    if(endstate[cur] == -1)
        dfs(cur - 1,sum,dp);
    else
    {
        int i;
        for(i = endstate[cur];i <= endstate[cur] + 1;i ++)
            dfs(cur - 1,sum + i * fac[dp],dp + 1);
    }
}

int getdigit(int a,int b)//从6进制数a中得到低位第b+1位
{
    int ret;
    a /= fac[b];
    ret = a % 6;
    return ret;
}

int main()
{
    int i,j,k;
    char s[3];
    while(scanf("%d%d",&y,&x),(x + y))
    {
        for(i = 0;i < 3;i ++)
        {
            for(j = 0;j < 3;j ++)
            {
                scanf("%s",s);
                switch(*s)
                {
                    case 'W':endstate[i * 3 + j] = 0;break;
                    case 'R':endstate[i * 3 + j] = 2;break;
                    case 'B':endstate[i * 3 + j] = 4;break;
                    case 'E':endstate[i * 3 + j] = -1;
                    endpos = i * 3 + j;break;
                }
            }
        }
        x --;
        y --;
        q[0].init();
        q[1].init();
        memset(flag,0,sizeof(flag));
        ss.pos = x * 3 + y;
        ss.state = ss.step = 0;
        q[0].push(ss);
        flag[0][0][ss.pos] = 1;
        dfs(8,0,0);//
        if(flag[1][q[0].top().state][q[0].top().pos])
        {
            printf("0\n");
            continue;
        }
        int ans = -1;
        int t1,t2;
        k = 0;
        for(j = 0;j < d1 && ans < 0;j ++)
        {
            for(i = 0;i <= 1;i ++)//2个队列  0正向搜  1反向搜
            {
                while(!q[i].empty())// && q[i].top().step == j)
                {
                    if(i)//反向
                    {
                        if(!(q[i].top().step == k && k < d2))
                            break;
                    }
                    else
                    {
                        if(!(q[i].top().step == j))
                            break;
                    }
                    now = q[i].top();
                    q[i].pop();
                    if(now.pos >= 3)//empty up
                    {
                        ss = now;
                        ss.step ++;
                        t1 = getdigit(now.state,10 - now.pos);
                        t2 = roll[t1][0];
                        ss.pos -= 3;
                        ss.state -= (t1 * fac[10 - now.pos]);//
                        t1 = getdigit(now.state,9 - now.pos);
                        ss.state -= (t1 * fac[9 - now.pos]);
                        ss.state += (t1 * fac[10 - now.pos]);
                        t1 = getdigit(now.state,8 - now.pos);
                        ss.state -= (t1 * fac[8 - now.pos]);
                        ss.state += (t1 * fac[9 - now.pos]);
                        ss.state += (t2 * fac[8 - now.pos]);//
                        if(!flag[i][ss.state][ss.pos])
                        {
                            flag[i][ss.state][ss.pos] = 1;
                            if(flag[1 - i][ss.state][ss.pos])
                            {
                                ans = i?j + k + 2:j + k + 1;
                                break;
                            }
                            q[i].push(ss);
                        }
                    }
                    if(now.pos <= 5)//empty down
                    {
                        ss = now;
                        ss.step ++;
                        ss.pos += 3;
                        t1 = getdigit(now.state,5 - now.pos);
                        t2 = roll[t1][0];
                        ss.state -= (t1 * fac[5 - now.pos]);
                        t1 = getdigit(now.state,6 - now.pos);
                        ss.state += (t1 * fac[5 - now.pos]);
                        ss.state -= (t1 * fac[6 - now.pos]);
                        t1 = getdigit(now.state,7 - now.pos);
                        ss.state += (t1 * fac[6 - now.pos]);
                        ss.state -= (t1 * fac[7 - now.pos]);
                        ss.state += (t2 * fac[7 - now.pos]);
                        if(!flag[i][ss.state][ss.pos])
                        {
                            flag[i][ss.state][ss.pos] = 1;
                            if(flag[1 - i][ss.state][ss.pos])
                            {
                                ans = i?j + k + 2:j + k + 1;
                                break;
                            }
                            q[i].push(ss);
                        }
                    }
                    if(now.pos % 3)//empty left
                    {
                        ss = now;
                        ss.step ++;
                        ss.pos --;
                        t1 = getdigit(now.state,8 - now.pos);
                        t2 = roll[t1][1];
                        ss.state -= (t1 * fac[8 - now.pos]);
                        ss.state += (t2 * fac[8 - now.pos]);
                        if(!flag[i][ss.state][ss.pos])
                        {
                            flag[i][ss.state][ss.pos] = 1;
                            if(flag[1 - i][ss.state][ss.pos])
                            {
                                ans = i?j + k + 2:j + k + 1;
                                break;
                            }
                            q[i].push(ss);
                        }
                    }
                    if((now.pos + 1) % 3)//empty right
                    {
                        ss = now;
                        ss.step ++;
                        ss.pos ++;
                        t1 = getdigit(now.state,7 - now.pos);
                        t2 = roll[t1][1];
                        ss.state -= (t1 * fac[7 - now.pos]);
                        ss.state += (t2 * fac[7 - now.pos]);
                        if(!flag[i][ss.state][ss.pos])
                        {
                            flag[i][ss.state][ss.pos] = 1;
                            if(flag[1 - i][ss.state][ss.pos])
                            {
                                ans = i?j + k + 2:j + k + 1;
                                break;
                            }
                            q[i].push(ss);
                        }
                    }
                }
                if(ans > 0)
                    break;
            }
            if(k < d2)
                k ++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
//正向12步   反向18步: .....
//正向13步   反向17步: Wrong Answer
//正向14步   反向16步: Wrong Answer
//正向15步   反向15步:56664K	4813MS
//正向16步   反向14步: 56892K	4000MS
//正向17步   反向13步: 57380K	2782MS
//正向18步   反向12步: 58192K	2016MS
//正向19步   反向11步: 51712K	1547MS
//正向20步   反向10步: 47212K	1282MS
//正向21步   反向9步: 45848K	1204MS    31340K	1204MS(循环队列开到100005)
//正向22步   反向8步: 47728K	1391MS
//正向23步   反向7步: 51968K	1704MS
//正向24步   反向6步: ...
//正向25步   反向5步: ...

/*

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
1 2
W W W
E W W
W W W
0 0
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值