八数码 || 九宫重排(A*搜索代码)

八数码 || 九宫重排在这里插入图片描述

废话:
这道题如果我们用bfs确实可以跑,但是大概率会炸掉,这道题是可以双向bfs,但今天我要展示的是用A*搜索的代码

策略分析:

1,标记:
既然是搜索,那我们就肯定就要加标记,来避免重复搜索,这里是九个格子,我们假定就是0,那么这个九宫格的每一个状态都可以用一个int数据来表示,这是我们很容易就想到了用 map<int,bool>映射作为标记我们已经访问过的点.

map<int,bool> vis;

2,A*启发式搜索:
A*的思想并不难,就是在选择下一个要搜索的点时,优先选择实际代价+估算代价最小的点来搜索,这里具体的A*思路我就不说了,这里有个视频讲得非常好,可以去看看:一看就懂的 A*寻路算法讲解 启发式搜索入门
3,估算距离:
这里的估算距离我用的是当前每个数字(除0以外,因为每次1-8的数字移动空格都会一起移动)到对应数字终点的曼哈顿距离即:

预估距离=|x1-x2|+|y1-y2|

4,优先队列与结构体:
要用A*来搜,当然就得会优先队列来存储准备搜索的点,由于个人爱好,我比较喜欢用重载小于符号的方式来写优先队列,step就是我们要用的实际代价(步数),juli是估算代价(估算的剩余步数),x当然就是代表的这个九宫格的数字位置状态嘛

struct cpp{
    int x,juli,step;
    bool operator<(const cpp &a)const{
        if(a.juli==juli && a.step==step)
            return x>a.x;
        return step+juli>a.step+a.juli;//由于优先队列是大的在前面
    }									//但我们为了让小的在前面就这样写
}tem;

priority_queue<cpp> dui;//优先队列

5,int数字状态转为char[3][3]数组:
我们把int转为char[3][3]数组当然是为了更好地理解代码,移动空格方块

inline void num_ju(int &a){
    for(int i=2;i>=0;i--){
        for(int j=2;j>=0;j--){
            tem_ju[i][j]=a%10;
            a/=10;
            if(tem_ju[i][j]==0)
                xx0=i,yy0=j;
        }
    }
}

6,char[3][3]数组转为int数字状态:

inline int ju_num(char a[][3]){
    int num=0;
    for(int i=0;i<3;i++){
        for(int j=0;j<3;j++){
            num*=10;
            num+=a[i][j];
        }
    }
    return num;
}

具体代码

欢迎各位在评论区指正或提问

#include <iostream>
#include <string>
#include<vector>
#include <algorithm>
#include<stdlib.h>
#include<cmath>
#include<map>
#include<stdio.h>
#include<queue>
using namespace std;
struct cpp{
    int x,juli,step;
    bool operator<(const cpp &a)const{
        if(a.juli==juli && a.step==step)
            return x>a.x;
        return step+juli>a.step+a.juli;
    }
}tem;

priority_queue<cpp> dui;
int kai,jie;
map<int,bool> vis;
char kai_ju[3][3],jie_ju[3][3],tem_ju[3][3];
int mhd_jie[10];
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int xx0,yy0;

void initi(){
    char kaitem[10],jietem[10];
    scanf("%s\n%s",kaitem,jietem);
    for(int i=0;i<9;i++){
        kai*=10,jie*=10;
        if(kaitem[i]>'0' && kaitem[i]<'9'){
            kai+=kaitem[i]-'0';
            kai_ju[i/3][i%3]=kaitem[i]-'0';
        }else{kai_ju[i/3][i%3]=0;}
        if(jietem[i]>'0' && jietem[i]<'9'){
            jie+=jietem[i]-'0';
            jie_ju[i/3][i%3]=jietem[i]-'0';
            mhd_jie[jietem[i]-'0']=i;
        }else{jie_ju[i/3][i%3]=0;}
    }
    vis[kai]=1,vis[jie]=1;
}

inline int mhd_juli(char a[][3]){
    int juli=0;
    for(int i=0;i<3;i++){
        for(int j=0;j<3;j++){
            if(a[i][j]!=0)
                juli+=abs(mhd_jie[(int)a[i][j]]/3-i)+abs(mhd_jie[(int)a[i][j]]%3-j);
        }
    }
    return juli;
}

inline void num_ju(int &a){
    for(int i=2;i>=0;i--){
        for(int j=2;j>=0;j--){
            tem_ju[i][j]=a%10;
            a/=10;
            if(tem_ju[i][j]==0)
                xx0=i,yy0=j;
        }
    }
}

inline int ju_num(char a[][3]){
    int num=0;
    for(int i=0;i<3;i++){
        for(int j=0;j<3;j++){
            num*=10;
            num+=a[i][j];
        }
    }
    return num;
}

int main(){
    initi();
    tem.x=kai,tem.juli=mhd_juli(kai_ju),tem.step=0;
    dui.push(tem);
    int res,xxx,yyy,tem_num,tem_step;
    while(!dui.empty()){
        res=dui.top().x;
        tem_step=dui.top().step;
        dui.pop();
        num_ju(res);
        for(int i=0;i<4;i++){
            xxx=xx0+dx[i],yyy=yy0+dy[i];
            if(xxx>=0 && xxx<3 && yyy>=0 && yyy<3){
                swap(tem_ju[xxx][yyy],tem_ju[xx0][yy0]);
                tem_num=ju_num(tem_ju);
                if(tem_num==jie){
                    printf("%d",tem_step+1);
                    return 0;
                }
                if(vis[tem_num]==0){
                    vis[tem_num]=1;
                    tem.x=tem_num;
                    tem.step=tem_step+1;
                    tem.juli=mhd_juli(tem_ju);
                    dui.push(tem);
                }
                swap(tem_ju[xx0][yy0],tem_ju[xxx][yyy]);
            }
        }
    }
    cout<<"-1";
    return 0;
}

题目链接:九宫重排

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值