Cubic Eight-Puzzle(UVA-1604)

news/2024/9/19 21:19:10 标签: 算法, bfs

网址如下:

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

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


http://www.niftyadmin.cn/n/5666158.html

相关文章

Linux平台UOS系统摄像头变焦功能

变焦功能也是摄像头常用的功能,摄像头有产品使用文档,因其过于繁琐,没有Demo,没有研究明白,故了解到一个在Linux平台通用的方式 v4l-utils 使用该工具可实现对摄像头的变焦、聚焦等多个功能实现,如下图: 其中zoom_absolute为焦距数值,可以看到支持的最大值、最小值、步…

走进低代码表单开发(五):高效开发的利器

前面我们已经介绍了勤研低代码开发平台的权限管理相关的内容&#xff0c;当表单设计完成后&#xff0c;我们将继续探索表单的其他功能&#xff0c;接下来&#xff0c;我们一起来看看勤研低代码平台还能如何为用户带来更便捷的开发体验。 一、表单导入 表单导入功能是勤研低代码…

C++ 条件变量:wait、wait_for、wait_until

前言 在C中&#xff0c;条件变量&#xff08;std::condition_variable&#xff09;是用来在多个线程之间同步执行流的一种机制。它们通常与互斥锁&#xff08;如std::mutex&#xff09;一起使用&#xff0c;以在特定条件满足时唤醒一个或多个线程。条件变量有三种使线程阻塞并…

设计模式之命令模式:从原理到实战,深入解析及源码应用

&#x1f3af; 设计模式专栏&#xff0c;持续更新中 欢迎订阅&#xff1a;JAVA实现设计模式 &#x1f6e0;️ 希望小伙伴们一键三连&#xff0c;有问题私信都会回复&#xff0c;或者在评论区直接发言 命令模式 什么是命令模式&#xff1f; 命令模式&#xff08;Command Pattern…

从黎巴嫩电子通信设备爆炸看如何防范网络电子袭击

引言&#xff1a; 在当今数字化时代&#xff0c;电子通信设备已成为我们日常生活中不可或缺的一部分。然而&#xff0c;近期黎巴嫩发生的电子设备爆炸事件提醒我们&#xff0c;这些设备也可能成为危险的武器。本文将深入探讨电子袭击的原理、防范措施&#xff0c;以及网络智能…

自学网络安全(黑客技术)_90天速成法

&#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“…

Laya2.x出包alipay小游戏

小游戏开发者工具&#xff0c;支付宝官方已经出了&#xff0c;不说了。 1.LAYA2.X打出得小游戏包中my-adapter.js这个文件需要替换&#xff0c;或者自行修改&#xff0c;替换3.x得&#xff1b; 2.unity导包出得模型文件命名需要注意&#xff0c;避免太长&#xff0c;路径也不…

【ShuQiHere】 支持向量机(SVM)详解:从理论到实践,这一篇就够了

&#x1f4d6; 【ShuQiHere】 在现代机器学习课程中&#xff0c;支持向量机&#xff08;SVM&#xff09; 是不可或缺的一部分。它不仅在分类任务中有出色表现&#xff0c;还能灵活处理回归问题。尽管看似复杂&#xff0c;SVM 背后的思想却深刻而优雅。今天我们将全面探讨**支持…