A*算法求解八数码问题

一、

问题描述:

A*算法是一种启发式图搜索算法,其特点在于对估价函数的定义上。对于一般的启发式图搜索,总是选择估价函数f值最小的节点作为扩展节点。因此,f是根据需要找到一条最小代价路径的观点来估算节点的,所以,考虑每个节点n的估价函数值为两个分量:f(n)=g(n)+h(n),从起始节点到节点n的实际代价g(n),从节点n到达目标节点的估价代价h(n)(不在点位个数),

八数码问题的求解

八数码问题是在3×3的九宫格棋盘上,摆有8个刻有1~8数码的将牌。棋盘中有一个空格,允许紧邻空格的某一将牌可以移到空格中,这样通过平移将牌可以将某一将牌布局变换为另一布局。给定的一种初始布局,目标状态,问如何移动将牌,实现从初始状态到目标状态的转变。

二、

算法设计:

将起始点加入open表

     当open表不为空时:

       寻找open表中f值最小的点current

       它是终止点,则找到结果,程序结束。

       否则,在Open表中移出current,对current表中的每一个临近点

           若它不可走或在close表中,略过

           若它不在open表中,加入。

           若它在open表中,计算g值,若g值更小,替换其父节点为current,更新

它的g值。

     若open表为空,则路径不存在。

算法流程图:

数据结构:用于记录最佳路径的树,两个链表,open表和close表

三、实现与实例

初始状态:2,8,3,0,6,4,1,7,5

目标状态:1,2,3,8,0,4,7,6,5

运行结果:

第0层的状态:

2 8 3

0 6 4

1 7 5

第1层的状态:

2 8 3

1 6 4

0 7 5

第2层的状态:

2 8 3

1 6 4

7 0 5

第3层的状态:

2 8 3

1 0 4

7 6 5

第4层的状态:

2 0 3

1 8 4

7 6 5

第5层的状态:

0 2 3

1 8 4

7 6 5

第6层的状态:

1 2 3

0 8 4

7 6 5

第7层的状态:

1 2 3

8 0 4

7 6 5

生成节点数目:20

扩展节点数目:10

运行时间:16.000000

代码实现:

#include "iostream"  

#include "stdlib.h"  

#include "conio.h"  

#include <math.h>

#include <windows.h>

using namespace std;

//定义二维数组来存储数据表示某一个特定状态  

typedef int status[3][3];

//定义 状态图中的结点数据结构  

typedef struct Node

{

    status data;//结点所存储的状态 ,一个3*3矩阵

    struct Node* parent;//指向结点的父亲结点  

    struct SpringLink* child;//指向结点的后继结点  

    struct Node* next;//指向open或者closed表中的后一个结点  

    int fvalue;//结点的估价函数值

    int gvalue;//结点的深度

    int hvalue;//结点到目标节点的预估代价值

}NNode, * PNode;

//定义存储指向结点后继结点的指针的地址  

typedef struct SpringLink

{

    struct Node* pointData;//指向结点的指针  

    struct SpringLink* next;//指向兄第结点  

}SPLink, * PSPLink;

PNode open;

PNode closed;

//OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点

//开始状态与目标状态  

int t = 0; //迭代次数,相当于运行时间

int count_extendnode = 0;//扩展结点

int count_sumnode = 0; //生成节点

status startt = { 2,8,3,0,6,4,1,7,5 }; //实验报告

status target = { 1,2,3,8,0,4,7,6,5 };

//初始化空链表  

void initLink(PNode& Head)

{

    Head = (PNode)malloc(sizeof(NNode));

    Head->next = NULL;

}

//判断链表是否为空  

bool isEmpty(PNode Head)

{

    if (Head->next == NULL)

        return true;

    else

        return false;

}

//链表中弹出一个数据  

void popNode(PNode& Head, PNode& FNode)

{

    if (isEmpty(Head))

    {

        FNode = NULL;

        return;

    }

    FNode = Head->next;

    Head->next = Head->next->next;

    FNode->next = NULL;

}

//向结点的最终后继结点链表中添加新的子结点  

void addSpringNode(PNode& Head, PNode newData)

{

    PSPLink newNode = (PSPLink)malloc(sizeof(SPLink));

    newNode->pointData = newData;

    newNode->next = Head->child;

    Head->child = newNode;

}

//释放状态图中存放结点后继结点地址的空间  

void freeSpringLink(PSPLink& Head)

include using namespace std; struct node{ int nodesun[4][4]; int pre; //上一步在队列中的位置 int flag ; //步数标识,表示当前的步数为有效的 int value; //与目标的差距 int x,y; //空格坐标 }queue[1000]; //移动方向数组 int zx[4]={-1,0,1,0}; int zy[4]={0,-1,0,1}; //当前步数 int top; int desti[4][4];//目标状态 int detect(struct node *p)//检查是否找到 {int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) return 0; return 1; } //打印 void printlj() {int tempt; int i,j; tempt=top; while(tempt!=0) { for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<queue[tempt].nodesun[i][j]; if(j==3) cout<<" "<<endl; } tempt=queue[tempt].pre; } } //现在状态与目标状态有多少个不同位置 int VALUE(struct node *p) {int count=0; int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) count++; return count; } void main() { //初始化 int i,j,m,n,f; int min=10; int temp,find=0,minnumber; top=1; for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; queue[1].nodesun[i][j]=temp; } cout<<"请输入初始状态的空格的位置(行)"<>temp; queue[1].x=temp; cout<<"请输入初始状态的空格的位置(列)"<>temp; queue[1].y=temp; queue[1].value=VALUE(&queue[1]); queue[1].pre=0; //上一步在队列中的位置 queue[1].flag=0; //目标状态 for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入目标状态第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; desti[i][j]=temp; } //根据估价函数 while(!find&&top>0) { for(i=1;i<=top;i++) //////////////////////////////////////////// //min为上一图中与目标图有多少个元素不相同,queue[i]为当前图与目标图有多少个元素不相同通过这两个数的比较,就可以得出当前图较之上一图向目标图接近同时把当前的i记录下来进行下一步比较 {if(queue[i].value<min&&queue[i].flag==0) {minnumber=i;// min=queue[i].value; //还有多少不同的位数 } } queue[minnumber].flag=1; //表示此位有效 ////////////////////////////////////// // for(f=0;f=1&&i=1&&j<=3) {top++; ///////////////////////////////////////////// //位置交换 queue[top]=queue[minnumber]; queue[top].nodesun[m][n]=queue[minnumber].nodesun[i][j]; queue[top].nodesun[i][j]=0; /////////////////////////////////////// //空格移动方向 queue[top].x=i; queue[top].y=j; /////////////////////////////////////// queue[top].pre=minnumber; //上一步在队列中的位置 queue[top].value=VALUE(&queue[top]); //有多少位与目标不同 queue[top].flag=0; //标识位初始化 if(detect(&queue[top])) //检查是否为目标 {printlj(); //打印 find=1; //设找到标识位 break; } } } } }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值