A*算法应用

评价函数

f(n)=g(n)+h(n)
n代表被评价的结点;
g(n)代表从初始结点s到结点n的最短路径的耗散值预测;
h(n)代表从结点n到目标结点g的最短路径的耗散值预测;
f(n)代表从初始节点s经过结点n到目标结点g的最短路径的耗散值预测。

当在算法A中的评价函数中使用的启发式函数h(n)是处在h*(n)的下界范围(h(n)<=h*(n))时,算法成为A*算法。

伪代码

while(OPEN!=NULL)
{
    从OPEN表中取f(n)最小的节点n;
    if(n节点==目标节点)
        break;
    for(当前节点n的每个子节点X)
    {
        计算f(X);
        if(XinOPEN)
            if(新的f(X)<OPEN中的f(X))
            {
                把n设置为X的父亲;
                更新OPEN表中的f(n);
            }
        if(XinCLOSE)
            continue;
        if(Xnotinboth)
        {
            把n设置为X的父亲;
            求f(X);
            并将X插入OPEN表中;//还没有排序
        }
    }//endfor
    将n节点插入CLOSE表中;
    按照f(n)将OPEN表中的节点排序;//实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。
}//endwhile(OPEN!=NULL)

八数码问题

八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
初始状态->目标状态:

这里写图片描述

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

struct knight{
    int maze[3][3];   //八数码具体情况
    int x,y;
    int Hash;
    int g,h,f;  // 估价函数名称
    bool operator < (const knight k) const{      //重载比较运算符
          return f > k.f;
     }
 }k;
 int tmp1[3][3];                   //初始状态和目标状态矩阵
 int visited[400000];                  //已访问标记(CLOSED列表)
 int previous[400000];           //路径保存
 int HASH[9]={1,1,2,6,24,120,720,5040,40320};   //HASH的权值
 int destination=131976;   //目标情况的HASH值
 /*
目标状态:
1 2 3
8   4
7 6 5
其hash值为131976
*/
 const int dirs[4][2]={{0,1},{0,-1},{1,0},{-1,0}};          //4个移动方向
 priority_queue<knight> que;                        //最小优先级队列(OPEN列表)

void debug1(const knight tmp){
    for(int i=0;i<3;i++){
        for(int j=0;j<3;j++)
            printf("%d ",tmp.maze[i][j]);
        printf("\n");
    }
    printf("%d %d\n%d %d\n",tmp.x,tmp.y,tmp.g,tmp.h);
    printf("hash=%d\n",tmp.Hash);
}
 void init()
 {
     printf("初始状态:\n");
         for(int i = 0; i < 3; i++)
         {
             for(int j = 0; j < 3; j++)
             {
                scanf("%d", &tmp1[i][j]);
                if(tmp1[i][j] == 0)
                {
                    k.maze[i][j] = 0;
                    k.x = i;
                    k.y = j;
                }
                else
                    k.maze[i][j] = tmp1[i][j];
             }

         }
 }
 bool in(const knight & a){                         //判断knight是否在边框内
     if(a.x < 0 || a.y < 0 || a.x >= 3 || a.y >= 3)
         return false;
     return true;
 }
 int get_h(const knight a){                    //估价函数
    int ans = 0;
    for(int i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
            if(a.maze[i][j])
                ans+=abs(i-(a.maze[i][j]-1)/3)+abs(j-(a.maze[i][j]-1)%3);
    return ans;
 }
int get_hash(const knight tmp){    //获得HASH值
    int a[9],kk=0;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            a[kk++]=tmp.maze[i][j];
    int res=0;
    for(int i=0;i<9;i++){
        int kk=0;
        for(int j=0;j<i;j++)
            if(a[j]>a[i])
                kk++;
        res+=HASH[i]*kk;
    }
    return res;
}
 void Astar(){          //A*算法
     knight u, v;
     while(!que.empty()){
         u=que.top();
         que.pop();
         for(int i = 0; i < 4; i++){
            v = u;
            v.x += dirs[i][0];
            v.y += dirs[i][1];
             if(in(v)){
                swap(v.maze[v.x][v.y], v.maze[u.x][u.y]);   //将空位和相邻位交换
                v.Hash = get_hash(v);             //得到HASH值
                if(visited[v.Hash] == -1){   //判断是否已遍历且是否可行,后者可以不要
                    visited[v.Hash] = i;           //保存方向
                    v.g++;;                  //已花代价+1
                    previous[v.Hash] = u.Hash;     //保存路径
                    v.h = get_h(v);           //得到新的估价函数H
                    v.f = v.h + v.g;
                    que.push(v);     //入队
                }
                if(v.Hash == destination)
                    return ;
             }
         }
     }
 }
void print(){
    int ans[100];
    int l = 0, ll = 1;
    int nxt = destination;
    //printf("%d",previous[nxt]);
    while(previous[nxt] != -1){  //从终点往起点找路径
        switch(visited[nxt]){   //四个方向对应
        case 0:ans[l++]=0;break;
        case 1:ans[l++]=1;break;
        case 2:ans[l++]=2;break;
        case 3:ans[l++]=3;break;
        }
        nxt = previous[nxt];
    }
    for(int i = l-1; i >= 0; i--)
    {
        if(ans[i] == 0)
            printf("%d:空格右边的数字挪动\n",ll++);
        else if(ans[i] == 1)
            printf("%d:空格左边的数字挪动\n",ll++);
        else if(ans[i] == 2)
            printf("%d:空格下边的数字挪动\n",ll++);
        else if(ans[i] == 3)
            printf("%d:空格上边的数字挪动\n",ll++);
    }
   printf("\n");
}
 int main()
 {
     int flag = 0;
     while(true)
     {
         printf("Case %d:\n", flag++);
         memset(visited,-1,sizeof(visited));
         memset(previous,-1,sizeof(previous));
         init();
         printf("哈希值:%d\n",get_hash(k));
         k.Hash = get_hash(k);
         if(k.Hash == destination){   //起始状态为目标状态
             printf("初始状态即为目标状态\n");
                continue;
            }
         visited[k.Hash] = -2;
         k.g = 0;
         k.h = get_h(k);
         k.f = k.h + k.g;
         while(!que.empty()) que.pop();
         que.push(k);
         Astar();
         print();
     }
     return 0;
 }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值