IDA*(八数码问题)

这是一种很奇特的算法。。

当然理解了以后就不那么奇特了。。

直接看题:

   3×3的棋盘上,摆有八个棋子,每个棋子上标有18的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

   样例输入:
    283104765

  友情样例输入图:

   

   样例输出:

4

  爱心样例解释:

   8下移一位,再将2右移一位,将1上移一位,最后将8左移一位。

  

  提示:搜索

思考:1.可以用BFS实现吗

      2.可以直接DFS

 显然不可以。。

 为什么呢?

先思考DFS

 DFS有一些很重要的东西。第一是边界,其次是一些剪枝之类的。很显然,这道题你不知道移动几步才能到达目标状态,也就是你无法定出一个边界,让你的程序停下来。

再思考BFS

 每一层都有四种选择,对应四种状态。不断扩展,直到有一种与目标状态相等,就结束,看似很完美的实现了。但我们深入思考一下,

你如何储存每一种状态,明显无法储存。

 

  针对这种没有边界的搜索题,就出现了A*IDA*(假定一个边界,进行搜索,若达到要求,就停止,否则将边界扩展一层,再次搜索),而IDA*A*的加强版,效率更高。其实和DFS相比就多了一句话,下面给出代码:

#include<bits/stdc++.h>

#pragma G++ optimize(2)

usingnamespacestd;

#define fz(i,j,k) b[i][j]=int(s[k]-'0')

strings;

int k,l,r;

int a[4][4]={0,0,0,0,0,1,2,3,0,8,0,4,0,7,6,5}//目标状态,b[4][4]={};

bool bd()//判断与目标状态是否相等

{

   for(int i=1;i<=3;i++)

    for(int j=1;j<=3;j++)

         {

              if(a[i][j]!=b[i][j])

               return0;

         }

   return1;

}

 

 

bool find(int x,int y,int s,int p)//x,y0所在的坐标,s为移动步数,p是一个剪枝思想,用来记录上一层移动的方向,如果这一层往回走,显然不是最优的。

{

   if(bd())

    {

       printf("%d",s);

       return1;

    }

   if(s==k)return0;

   if(x>1&&p!=1)

    {

       b[x][y]=b[x-1][y];

       b[x-1][y]=0;

       if(find(x-1,y,s+1,2))

               {

               b[x-1][y]=b[x][y];

         b[x][y]=0;

                return1;

         }

        b[x-1][y]=b[x][y];

       b[x][y]=0;

    }

   if(x<3&&p!=2)

    {

       b[x][y]=b[x+1][y];

       b[x+1][y]=0;

       if(find(x+1,y,s+1,1))

              {

               b[x+1][y]=b[x][y];

        b[x][y]=0;

               return1;

        }

              b[x+1][y]=b[x][y];

       b[x][y]=0;

    }

   if(y>1&&p!=3)

    {

       b[x][y]=b[x][y-1];

       b[x][y-1]=0;

       if(find(x,y-1,s+1,4))

              {

               b[x][y-1]=b[x][y];

        b[x][y]=0;

               return1;

        }

              b[x][y-1]=b[x][y];

       b[x][y]=0;

    }

   if(y<3&&p!=4)

    {

       b[x][y]=b[x][y+1];

       b[x][y+1]=0;

       if(find(x,y+1,s+1,3))

              {

               b[x][y+1]=b[x][y];

        b[x][y]=0;

               return1;

        }

              b[x][y+1]=b[x][y];

       b[x][y]=0;

    }

   return0;

}

 

int main()

{

       cin>>s;

       int p=0;

       for(int i=0;i<s.size();i++)

        {

              k=i+1;int t;

              if(k%3==0)t=3,k--;

               else t=k%3;

               fz(k/3+1,t,i);

               if(b[k/3+1][t]!=a[k/3+1][t]) p++;//

              if(b[k/3+1][t]==0) l=k/3+1,r=t;

        }

       for(k=p/2;!find(l,r,0,0);k++);//DFS有区别的地方,k即为假定的边界,p/2是一个略微的估价,因为一次交换最多修改两个不等的方块,p为统计出来与目标不等的方块数,那么至少p/2次到达目标。

       return0;

}

  感悟:IDA*A*高效,且写起来比较简单,还容易理解,不知道要A*干什么的。。


猜你喜欢

转载自blog.csdn.net/qq_40716114/article/details/80396362