翻题解的时候看到大佬们都打的双向广搜。(蒟蒻在墙角瑟瑟发抖
在这里提供一种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; //愉快结束 }
好像跑的挺快的
双向广搜是不可能打的,这辈子都不可能打的