转自:https://www.cnblogs.com/fujudge/p/7398153.html
上一次介绍过dbfs版本,这次来介绍idA*版本。
首先要理解idA算法的思想,是将迭代加深与A的结合,将估价函数h(n)作为迭代的限制值,进行dfs。
(A*和迭代加深的介绍等有时间再写出来吧)
对所有点(除0以外的)进行曼哈顿距离计算(目标状态到初始状态),h(n)为当前节点的各点的曼哈顿距离和。
在代码中看:
#include
#include
#include
#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 的办法使用,可以达到优化时间的作用。