A*算法与8数码问题

来源:互联网 发布:淘宝怎么看购物车数量 编辑:程序博客网 时间:2024/04/29 19:22

A*与八数码问题

最近自学A*,看来很多神犇程序,似乎都很烦,今天我要来谈谈A*8数码问题

先给个例题:

Description

八 方块移动游戏要求从一个含8个数字(用1-8表示)的方块以及一个空格方块(用0表示)的3×3矩阵的起始状态开始,不断移动该空格方块以使其和相邻的方 块互换,直至达到所定义的目标状态。空格方块在中间位置时有上、下、左、右4个方向可移动,在四个角落上有2个方向可移动,在其他位置上有3个方向可移 动。例如,假设一个3×3矩阵的初始状态为:



另外,在所有可能的从初始状态到目标状态的移动路径中,步数最少的路径被称为最短路径;在上面的例子中,最短路径为5。如果不存在从初始状态到目标状态的任何路径,则称该组状态无解。 请设计有效的算法找到从八方块的某初始状态到某目标状态的所有可能路径中的最短路径。

Input

程序需读入初始状态和目标状态,这两个状态都由9个数字组成(0表示空格,1-8表示8个数字方块)。

Output

如果输入数据有解,输出一个表示最短路径的非负的整数;如果输入数据无解,输出-1。

Sample Input

803214765
123804765

Sample Output

5

好了,题目看起来怎么瞎搞都行,但是效率值得考虑。

这里我使用最佳优先搜索策略:

它是将深度优先和广度优先搜索优点进行组合而成的一种简单方法。
在最佳优先搜索中,有一个评价函数,总是选择目前为止产生的所有结点中具有最小代价的结点。
因此最佳优先搜索具有全局观点。


以上述问题为例:


步骤一  . 使用评价函数构造一个堆,首先构造由根结点组成的一元堆。

步骤二  . 考察堆顶元素是否为目标结点。如果是,算法停止;否则转向步骤三。

步骤三  . 删除堆顶元素并扩展堆顶,增加该元素的后代到堆中。

步骤四  . 如果堆为空,那么失败;否则,转向步骤二。


好了理论先放一边,下面贴代码:

#include <iostream>#include <vector>#include <bitset>#include <cmath>#include <iomanip>#include <string>#include <string.h>#include <time.h>#include <memory.h>#include <stdio.h>#include <cmath>#include <stdlib.h>#include <algorithm>#include <stack>#include <queue>#include <cstdio>#include <climits>#define intmin -2147483640#define intmax 2147483640using namespace std;const int Cantor_Expansion_MAXN=3628799+100;const int Move_Tag_X[7]={0,1,-1,0};const int Move_Tag_Y[7]={1,0,0,-1};const int Cantor_Num[15]={0,1,2,6,24,120,720,5040,40320,362880,3628800};typedef struct Gentil_HP_Node{    int num[4][4];    int Step,Bound;}node;bool operator>(const struct Gentil_HP_Node a,const struct Gentil_HP_Node b){return a.Bound>b.Bound;}priority_queue < node,vector<node>,greater<node> > Q;node StMode,EdMode;bool Cantor[Cantor_Expansion_MAXN];int Bound_Cantor_Expansion (const node S)       //康托展开,自行百度{    return Cantor_Num[9]*S.num[1][1]+           Cantor_Num[8]*S.num[1][2]+           Cantor_Num[7]*S.num[1][3]+           Cantor_Num[6]*S.num[2][1]+           Cantor_Num[5]*S.num[2][2]+           Cantor_Num[4]*S.num[2][3]+           Cantor_Num[3]*S.num[3][1]+           Cantor_Num[2]*S.num[3][2]+           Cantor_Num[1]*S.num[3][3];}void Put_Num_Into_Node (int x,node &S){     S.num[3][3]=x%10;x/=10;     S.num[3][2]=x%10;x/=10;     S.num[3][1]=x%10;x/=10;     S.num[2][3]=x%10;x/=10;     S.num[2][2]=x%10;x/=10;     S.num[2][1]=x%10;x/=10;     S.num[1][3]=x%10;x/=10;     S.num[1][2]=x%10;x/=10;     S.num[1][1]=x%10;x/=10;}bool Judge_In_Board (int x,int y) {     if (x>=1 && y>=1 && x<4 && y<4)     return 1;     return 0;}void init (){     int Stnum,Ednum;     scanf ("%d%d",&Stnum,&Ednum);     Put_Num_Into_Node (Stnum,StMode);     Put_Num_Into_Node (Ednum,EdMode);     StMode.Step=0;}int Bound_for_Node (node S,node T)         //估价函数{    int Num=0;    if (S.num[3][3]!=T.num[3][3]) Num++;    if (S.num[3][2]!=T.num[3][2]) Num++;    if (S.num[3][1]!=T.num[3][1]) Num++;    if (S.num[2][3]!=T.num[2][3]) Num++;    if (S.num[2][2]!=T.num[2][2]) Num++;    if (S.num[2][1]!=T.num[2][1]) Num++;    if (S.num[1][3]!=T.num[1][3]) Num++;    if (S.num[1][2]!=T.num[1][2]) Num++;    if (S.num[1][1]!=T.num[1][1]) Num++;    return Num+S.Step;}void Out_For_Test (const node X){     cout<<X.num[1][1]<<" "<<X.num[1][2]<<" "<<X.num[1][3]<<endl;     cout<<X.num[2][1]<<" "<<X.num[2][2]<<" "<<X.num[2][3]<<endl;     cout<<X.num[3][1]<<" "<<X.num[3][2]<<" "<<X.num[3][3]<<endl<<endl;}void A_Star_Search (){     node Now,next;     StMode.Bound=(Bound_for_Node (StMode,EdMode));     Q.push (StMode);     Cantor[Bound_Cantor_Expansion (StMode)]=1;          int ans=intmax;     int i,j,k,sx,sy,tmp;     while (!Q.empty ())     {         Now=Q.top ();Q.pop ();         if (Now.Bound==Now.Step){ans=min (Now.Step,ans);break;}                  for (i=1;i<4;i++)         for (j=1;j<4;j++)         {             if (Now.num[i][j]==0)             {                 for (k=0;k<4;k++)                 {                     next=Now;                     next.Step ++;                     sx=i+Move_Tag_X[k],sy=j+Move_Tag_Y[k];                     if (Judge_In_Board (sx,sy))                     {                         next.num[i][j]+=next.num[sx][sy];                         next.num[sx][sy]-=next.num[i][j];                         tmp=Bound_Cantor_Expansion (next);                         if (!Cantor[tmp])                         {                             Cantor[tmp]=1;                             next.Bound=Bound_for_Node (next,EdMode);                             Q.push (next);                         }                     }                 }                 goto Loop;             }         }         Loop: ;     }          if (ans==intmax)  printf ("-1");     else   cout<<ans;}int main(){    init ();    A_Star_Search ();}


上题中用到了一个估价函数,所谓估价函数就是对最优解的估计。

我们不妨用g(n)  表示从解空间树的根到结点n的路径长度。

h*(n)表示从结点n到目标结点的最优路径长度。

结点n的代价f*(n) = g(n) + h*(n)

尽管有许多方法估计h*(n),但A*算法保证总是使h(n)  ≤ h*(n)。也就是说,应该使用h*(n)的保守估计。这意味着,在A*算法中,总是使用

f(n) = g(n) + h(n) ≤ g(n) + h*(n) = f*(n) 作为估价函数。

 

注意到A*算法采用最佳优先规则,这意味着在所有将扩展的结点中,选择具有最小代价的结点作为下一扩展结点。

A*算法具有下列终止规则:如果被选择的结点也是目标结点,那么这个结点代表一个最优解并且过程可被终止。

证明直接COPY一下课件:



最后给出例题一道:

http://poj.org/problem?id=2243


今天就说到这,希望大家点个赞。


0 0
原创粉丝点击