翻题解的时候看到大佬们都打的双向广搜。(蒟蒻在墙角瑟瑟发抖
在这里提供一种IDA*解法,
还是一层层地搜索,如果超出预期层数就返回
然后用评估函数进行剪枝(就是可行性剪枝)感觉写起来比双向广搜好多了
评估函数越好,搜索效率越高;
如果评估函数太差,那IDA*和普通爆搜复杂度差不多。
所有一个好的评估函数很关键;
主要是看这道题评估函数(h)怎么写,
对于评估函数想到的有两个方案:
方案一:不在自己应在位置上数的个数;
方案二:所有数距自己应在位置的曼哈顿距离之和;
很显然选第二个方案更优,(虽然方案一也可以AC)
因为一个数移动到自己目标位置,至少要移动它现在位置到它目标位置的曼哈顿距离
例如下面这一个
它的距原状态的曼哈顿距离和就是4(1+1+2)
(不用计算0,因为其它数字归位后,0也会回到原位)
至于如何实现,可以先开一个数组记录任意两位置之间的曼哈顿距离
然后再开一个数组记录每个数字目标状态的位置
大概是这样的
int dis[9][9]={
{0,1,2,1,2,3,2,3,4},
{1,0,1,2,1,2,3,2,3},
{2,1,0,3,2,1,4,3,2},
{1,2,3,0,1,2,1,2,3},
{2,1,2,1,0,1,2,1,2},
{3,2,1,2,1,0,3,2,1},
{2,3,4,1,2,3,0,1,2},
{3,2,3,2,1,2,1,0,1},
{4,3,2,3,2,1,2,1,0}
}; //两个位置间的曼哈顿距离
int yuan[9]={4,0,1,2,5,8,7,6,3};//每个数字的目标位置
int chu[3][3];//存储现在状态
int h()
{
int ans=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
if(chu[i][j]==0) continue;
ans+=dis[yuan[chu[i][j]]][i*3+j];//i*3+j表示数字现在位置
}
return ans;
}
这样我们的评估函数就写好啦!
然后搜索。
具体细节在代码里说明。(码风有点丑,见谅QAQ)
#include<bits/stdc++.h>
using namespace std;
int dis[9][9]={
{0,1,2,1,2,3,2,3,4},
{1,0,1,2,1,2,3,2,3},
{2,1,0,3,2,1,4,3,2},
{1,2,3,0,1,2,1,2,3},
{2,1,2,1,0,1,2,1,2},
{3,2,1,2,1,0,3,2,1},
{2,3,4,1,2,3,0,1,2},
{3,2,3,2,1,2,1,0,1},
{4,3,2,3,2,1,2,1,0}
}; //两个位置间的曼哈顿距离
int yuan[9]={4,0,1,2,5,8,7,6,3};//每个数字的目标位置
int chu[3][3];//存储现在状态
inline int h()
{
int ans=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
if(chu[i][j]==0) continue;//不考虑0
ans+=dis[yuan[chu[i][j]]][i*3+j];//i*3+j表示数字现在位置
}
return ans;
}
int ax[4]={0,-1,1,0};
int ay[4]={1,0,0,-1};//这两个数组一定要对称写,使搜索时不会返回上一状态
bool flag;
void dfs(int ceng,int dep,int f,int x,int y)//f表示上一次转移的方向
{
int nx,ny;
if(ceng==dep) //如果搜到了预期层数
{
if(!h()) flag=1;//如果达到了目标状态
return;
}
for(int i=0;i<4;i++)
{
nx=x+ax[i];
ny=y+ay[i];
if(nx<0||ny<0||nx>=3||ny>=3) continue;//出界
if(i+f==3) continue;//如果会返回上一个状态
swap(chu[nx][ny],chu[x][y]);
if(ceng+h()<=dep)//如果最优情况都会超出预期,就直接返回
dfs(ceng+1,dep,i,nx,ny);
if(flag) return;//找到解就直接返回
swap(chu[nx][ny],chu[x][y]);//回溯
}
return;
}
int main()
{
char k;
int sx,sy;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
cin>>k;
chu[i][j]=k-'0';
if(k=='0')
{
sx=i;
sy=j;//初始0的位置
}
}
}
if(!h())//特判,如果一开始就达到目标,直接结束
{
cout<<0;
return 0;
}
flag=0;
int maxdep;//预期深度
for(maxdep=1;;maxdep++)//一层一层地搜
{
dfs(0,maxdep,-1,sx,sy);//一开始没有方向,所以传入-1
if(flag)//找到解
{
cout<<maxdep<<endl;
break;
}
}
return 0;
//愉快结束
}
好像跑的挺快的
双向广搜是不可能打的,这辈子都不可能打的