八数码问题强化版:十五数码问题idA*版本
---恢复内容开始---
上一次介绍过dbfs版本,这次来介绍idA*版本。
首先要理解idA*算法的思想,是将迭代加深与A*的结合,将估价函数h(n)作为迭代的限制值,进行dfs。
(A*和迭代加深的介绍等有时间再写出来吧)
对所有点(除0以外的)进行曼哈顿距离计算(目标状态到初始状态),h(n)为当前节点的各点的曼哈顿距离和。
在代码中看:
#include<cstdio> #include<algorithm> #include<cstring> #define abs(w) (w>=0?w:-(w)) int xx[20],yy[20],bound,flg;int u[4] = {0,0,1,-1}; int p[4] = {1,-1,0,0}; using namespace std; struct node{ int mat[20]; int pos; int H()//计算h(n) { register int ans(0); for(register int i = 0 ; i < 15 ; ++i) ans+=abs(xx[mat[i]]-(i>>2))+abs(yy[mat[i]]-(i&3)); return ans; } bool check()//判断解的存在性 { register int tot(0),i,j; for( i = 0 ; i < 16 ; ++i) { if(!mat[i])continue; for(j = 0 ; j < i ; ++j) if(mat[j]<mat[i])++tot; } tot+=abs((pos>>2)-3)+abs((pos&3)-3); if(tot&1)return true; return false; } }a; inline bool ok(register int x,register int y)//防止从走过来的地方再退回去 { if(x>y)swap(x,y); if(x==0&y==1)return false; if(x==2&&y==3)return false; return true; } int dfs(register int step,register int h,register int las) {if(step+h>bound)return step+h;//如果g(n)+h(n)>f(n),就更新f(n) if(!h)//到达最终状态,输出g(n)即可 { printf("%d",step); flg=1; return step; } register int ret=127,pos=a.pos,x=pos>>2,y=pos&3; register int dx,dy,tar,ht,tmp,i; for(i = 0 ; i < 4 ; ++i)//向四个方向拓展 { dx=x+u[i]; dy=y+p[i]; if(dx<0||dy<0||dx>3||dy>3||!ok(i,las))continue; tar=(dx<<2)|dy;//计算拓展出新节点的一维坐标 a.mat[pos]=a.mat[tar]; a.mat[tar]=0;//这两行相当于swap操作(据说这样可以快一点) a.pos=tar; ht=h-(abs(xx[a.mat[pos]]-dx)+abs(yy[a.mat[pos]]-dy)) + abs(xx[a.mat[pos]]-x)+abs(yy[a.mat[pos]]-y) ;//计算新的h值 tmp=dfs(step+1,ht,i); if(flg)return tmp;//找到路径 if(ret>tmp)ret=tmp;//更新bound a.mat[tar]=a.mat[pos];//回溯 a.mat[pos]=0; a.pos=pos; } return ret; } int main() { // freopen("fifteen.in","r",stdin); // freopen("fifteen.out","w",stdout); register int k,i; for( i = 0 ; i < 16 ; ++i) { scanf("%d",&k); if(!k)a.pos=i;//记录0的位置 else { a.mat[i]=k; xx[k]=i>>2;//保存k的二维坐标 yy[k]=i&3;//相当于i%4 } }if(!a.check())//判断解的存在性 { printf("No"); return 0; } for( i = 0 ; i < 16 ; ++i)//从目标状态向初始状态更新,别问我为什么,看各方大佬的代码都是这样(据说是一个小技巧,可以快一点)。 a.mat[i]=i+1; a.mat[15]=0; a.pos=15; for(bound=a.H();bound<=60;bound=dfs(0,a.H(),4))//idA*部分 if(flg)break; return 0; }
解释一下这个代码中的一些优化的小细节(数据好像有卡常数的):
1、inline 以及register
inline 所谓的内联函数,据说可以优化时间。
register 将空间存在CPU的寄存器中,优化时间。
借鉴网友的例子,作用相当于口袋中有面包,就不需要跑到百米开外的商店去买,自然时间就快。
2、i&3相当于i%4
因为&3相当于保存二进制位的最后三位,即实现了%4的操作,但只适用于%2,4,8这样的2^n数。
3、对于abs的define 将一些在代码中重复出现的小函数可以采用define 的办法使用,可以达到优化时间的作用。