八数码 || 九宫重排![在这里插入图片描述](https://img-blog.csdnimg.cn/20210524191138566.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3MDczOTM0,size_16,color_FFFFFF,t_70)
废话:
这道题如果我们用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;
}
题目链接:九宫重排