一个比较经典的题,相信小伙伴们都玩过九宫格八个块的拼图游戏,这道题可以看成是这个游戏的抽象化,每个拼图块抽象成一个数字,求还原拼图,即将9个数字复位的最小步数,这道题给的初始状态,即要还原的状态是“123/804/765”,我在这里先讲最简单的一个算法IDA*,看到网上大佬们用双向搜hash判重,set判重等等等等,身为小蒟蒻的我,瑟瑟发抖,然而我这道题IDA*完美水过,下面讲思路
第一步,准备,一个map二位数组存图,一个mmp数组用来每次搜索,now数组表示0~8的数字所在的位置,这里的位置是1~9,然后是hope数组,预处理好,hope里存的是每个数字应该在的位置,然后是dis数组,存的是每两个点之间换过去需要的步数,还有两个方向数组,dfs扩展用
第二步,读入,先读入一个字符串,定义一个cnt=0,下面两重循环,每层3位,模拟九宫格,用cnt来循环字符串,cnt+1即为当前位的数字的now,即当前位置(1~9),然后如果当前的数字是零,那么用一个stx,sty存储它对应的i,j,因为我们要从空格子开始移动
第三步,IDA*,lim限制深度不用说,每次进IDA*之前,将map数组memcpy到mmp中,用mmp来模拟交换,传参传三个,当前深度,当前点横纵坐标,然后模拟当前点与他的上下左右交换
第四步,剪枝,用一个fuck=0,加上每个点当前位置到他的hope位置的dis值,如果当前IDA*的deep加上fuck>lim,那么当前搜索无解,return ,如果fuck=0,那么所有点回到了原位,return 1;
代码
By Acer.Mo
#include
#include
#include
#include
#include
#include
using namespace std;
int map[5][5],mmp[5][5];//两个地图
int lim,stx,sty,cnt=0;
int dis[10][10]=
{
{0,0,0},
{0,0,1,2,1,2,3,2,3,4},
{0,1,0,1,2,1,2,3,2,3},
{0,2,1,0,3,2,1,4,3,2},
{0,1,2,3,0,1,2,1,2,3},
{0,2,1,2,1,0,1,2,1,2},
{0,3,2,1,2,1,0,3,2,1},
{0,2,3,4,1,2,3,0,1,2},
{0,3,2,3,2,1,2,1,0,1},
{0,4,3,2,3,2,1,2,1,0}
};//预处理的dis
int fx[5]={0,0,1,-1},fy[5]={1,-1,0,0};
int now[10],hope[10]={5,1,2,3,6,9,8,7,4};//期望位置与当前位置
int cutdown()
{
int fuck=0;
for(int i=1;i<=8;i++) fuck+=dis[now[i]][hope[i]];
return fuck;
}//剪枝
int IDAS(int limdep,int sx,int sy)
{
if (limdep+cutdown()>lim) return 0;
if (cutdown()==0) return 1;
for (int i=0;i<4;i++)
{
int xx=sx+fx[i];
int yy=sy+fy[i];
if (xx>3||yy>3||xx<1||yy<1) continue;
swap(mmp[xx][yy],mmp[sx][sy]);//交换两个块地图中的位置
swap(now[mmp[xx][yy]],now[mmp[sx][sy]]);//下标位置
if (IDAS(limdep+1,xx,yy)) return 1;
swap(mmp[xx][yy],mmp[sx][sy]);//回溯
swap(now[mmp[xx][yy]],now[mmp[sx][sy]]);//同理
}
return 0;
}
int main()
{
string s;
cin>>s;
for (int i=1;i<=3;i++)
{
for (int k=1;k<=3;k++)
{
map[i][k]=s[cnt++]-'0';
now[map[i][k]]=cnt;//cnt循环字符串下标
if (map[i][k]==0) stx=i,sty=k;//起点坐标
}
}
for (lim=0;;lim++)
{
memcpy(mmp,map,sizeof(map));
if (IDAS(0,stx,sty)) break;
} //IDA*
printf("%d",lim);
return 0;
}
小伙伴的双向搜索+hashhttps://blog.csdn.net/qq_41570588/article/details/79921976