八数码问题强化版:十五数码问题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 的办法使用,可以达到优化时间的作用。

posted @ 2017-08-19 22:16  傅judge  阅读(1536)  评论(0编辑  收藏  举报