Cubic Eight-Puzzle(UVA-1604)

网址如下:

Cubic Eight-Puzzle - UVA 1604 - Virtual Judge (vjudge.net)

(第三方网站)

AC了!本来以为会TLE的

因为当时已经把我能想到的优化方法都加上去了,可是对于深度有30及以上的样例,在我的电脑上运行也是需要差不多一秒的

但是上交之后只花了380ms,AC了

代码如下:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>

const int maxState = 7;
const int maxn = 3;
const int maxStep = 30;

struct Node{
    int x, y, d;
    int cube[3][3];
};

int nxtx[4]{-1, 1, 0, 0};//上,下,左,右
int nxty[4]{0, 0, -1, 1};
int hor[maxState]{0, 3, 6, 1, 5, 4, 2};//横向移动
int ver[maxState]{0, 5, 4, 6, 2, 1, 3};//纵向移动
int target[maxn][maxn];//目标颜色
char vis[maxState][maxState][maxState][maxState][maxState][maxState][maxState][maxState][maxState];
int x, y, t_x, t_y;

bool is_reached(const Node &u){
    for(int i = 0; i < maxn; i++)
        for(int j = 0; j < maxn; j++)
            if(target[i][j] != (u.cube[i][j] + 1) / 2) return false;
    return true;
}
int bfs(void){
    Node base; base.x = x; base.y = y; base.d = 0;
    for(int i = 0; i < maxn; i++)
        for(int j = 0; j < maxn; j++)
            base.cube[i][j] = 1;
    base.cube[base.x][base.y] = 0;
    std::queue<Node> q; q.push(base);
    while(!q.empty()){
        Node u = q.front(); q.pop();
        //检查
        if(is_reached(u)) return u.d;
        if(vis[u.cube[0][0]][u.cube[0][1]][u.cube[0][2]][u.cube[1][0]][u.cube[1][1]][u.cube[1][2]][u.cube[2][0]][u.cube[2][1]][u.cube[2][2]] == 1) continue;
        vis[u.cube[0][0]][u.cube[0][1]][u.cube[0][2]][u.cube[1][0]][u.cube[1][1]][u.cube[1][2]][u.cube[2][0]][u.cube[2][1]][u.cube[2][2]] = 1;
        if(u.d > 21) return -1;
        //下一步
        for(int i = 0; i < 4; i++){
            int _x = u.x + nxtx[i], _y = u.y + nxty[i];
            if(_x < 0 || _x > 2 || _y < 0 || _y > 2) continue;
            Node v = u;
            v.x = _x; v.y = _y; v.cube[v.x][v.y] = 0; v.d = u.d + 1;
            if(i < 2)
                v.cube[u.x][u.y] = ver[u.cube[_x][_y]];//纵向移动
            else
                v.cube[u.x][u.y] = hor[u.cube[_x][_y]];//横向移动
            q.push(v);
        }
    }
    return 0;
}//正向bfs
void init_push(std::queue<Node> &q, Node &base, int i, int j){
    base.cube[i][j] = target[i][j] * 2;
    if(i == 2 && j == 2) q.push(base);
    else init_push(q, base, (j + 1) == 3 ? i + 1 : i, (j + 1) % 3);
    if(base.cube[i][j]){
        base.cube[i][j] -= 1;
        if(i == 2 && j == 2) q.push(base);
        else init_push(q, base, (j + 1) == 3 ? i + 1 : i, (j + 1) % 3);
    }
}
int rbfs(void){
    Node base; base.x = t_x; base.y = t_y; base.d = 0;
    std::queue<Node> q;
    init_push(q, base, 0, 0);
    while(!q.empty()){
        Node u = q.front(); q.pop();
        //检查
        if(vis[u.cube[0][0]][u.cube[0][1]][u.cube[0][2]][u.cube[1][0]][u.cube[1][1]][u.cube[1][2]][u.cube[2][0]][u.cube[2][1]][u.cube[2][2]] == 1) return u.d + 21;
        if(vis[u.cube[0][0]][u.cube[0][1]][u.cube[0][2]][u.cube[1][0]][u.cube[1][1]][u.cube[1][2]][u.cube[2][0]][u.cube[2][1]][u.cube[2][2]] == -1) continue;
        vis[u.cube[0][0]][u.cube[0][1]][u.cube[0][2]][u.cube[1][0]][u.cube[1][1]][u.cube[1][2]][u.cube[2][0]][u.cube[2][1]][u.cube[2][2]] = -1;
        if(u.d > 9) return -1;
        //下一步
        for(int i = 0; i < 4; i++){
            int _x = u.x + nxtx[i], _y = u.y + nxty[i];
            if(_x < 0 || _x > 2 || _y < 0 || _y > 2) continue;
            Node v = u;
            v.x = _x; v.y = _y; v.cube[v.x][v.y] = 0; v.d = u.d + 1;
            if(i < 2)
                v.cube[u.x][u.y] = ver[u.cube[_x][_y]];//纵向移动
            else
                v.cube[u.x][u.y] = hor[u.cube[_x][_y]];//横向移动
            q.push(v);
        }
    }
    return 0;
}//逆向bfs
void input(void){
    char c;
    for(int i = 0; i < maxn; i++)
        for(int j = 0; j < maxn; ){
            c = getchar();
            switch(c)
            {
            case 'W':
                target[i][j++] = 1;
                break;
            case 'B':
                target[i][j++] = 2;
                break;
            case 'R':
                target[i][j++] = 3;
                break;
            case 'E':
                target[i][j] = 0;
                t_x = i, t_y = j++;
                break;
            default:
                break;
            }
        }
}

int main(void)
{
    while(scanf("%d%d", &y, &x) == 2 && x){
        memset(vis, 0, sizeof(vis)); x--; y--;
        input();
        int ans = bfs();
        if(ans == -1) ans = rbfs();
        printf("%d\n", ans);
    }

    return 0;
}

解释一下代码的一些方面:

首先是方块的状态表示:

0代表此处为空(E)

1代表正上方为W,正面为R,侧面为B

2代表正上方为W,正面为B,侧面为R

3代表正上方为B,正面为R,侧面为W

4代表正上方为B,正面为W,侧面为R

5代表正上方为R,正面为W,侧面为B

6代表正上方为R,正面为B,侧面为W

颜色和数字的关系:

0-E

1-W

2-B

3-R

则可以得出 方块正上方的颜色 = (方块的状态+1) / 2

(当然是用数字表示)

然后是方块转动后的变化:

int hor[maxState]{0, 3, 6, 1, 5, 4, 2};//横向移动
int ver[maxState]{0, 5, 4, 6, 2, 1, 3};//纵向移动

直接用数组记录下来了,下标代表原方块的状态

总的思路是bfs,用vis来记录已经出现过的状态(所有方块),但是为了缩短搜索的深度(总所周知,bfs的结点数量是随深度增加而指数级增长的(按最坏情况下一个结点可以扩展出4个结点))

所以可以用到双向bfs来把深度直接减少一半,其中,正向搜索的状态记录在vis中的值为1,逆向搜索的为-1

但是考虑到终点状态只给出了颜色,没给出方块状态,所以逆向bfs有两个思路:

1、直接用颜色表示,初始结点为1个,但是一个结点可以延申出8个结点

2、和正向bfs一样用方块状态表示,初始结点有2^8=256个,一个结点可以延申出4个结点

可以计算出在搜索深度在9以内的时候,法1的效率优于法2,但是考虑到法1在处理vis数组的时候更加麻烦,我是选择了法2,这样除了vis的值和初始结点不同之外,逆向搜索可以直接copy正向搜索

顺带一提,这里没有和普通的双向bfs一样,正向搜索一次,逆向搜索一次,也是考虑到了使用了法2的逆向搜索的初始结点太多,先尽量用正向搜索,最后再用逆向搜索

而且,可以注意到我的vis的数据类型为char,而不是int,是因为char可以当成整型来用,而且只需要1字节,即使是short也要占用2字节,就算是1字节,vis数组内存占用也有34MB左右

再谈谈我的思路的变化

我刚开始是想用二进制来进行优化的,因为如果加上未定义这个状态(实际上没什么鸟用),方块的状态有8个,可以用二进制的三个位来表示,而用一个int就可以表示9个方块一起的状态,妙啊!

而实际上,因为bfs在延申到下一个结点的时候需要将这个int值拆成9个值来计算,所以还不如直接用9个值来记录更快

而且,有一个致命的问题,若一个状态占一个字节,有8^9个状态,内存占用高达128MB,所以我没用vis数组,结果可想而知,运行超慢

后面是减了一个状态,就可以用上vis数组了,但是感觉还不是很快,就想到用双向bfs了

最后代码就改成了上面那样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值