关于A*(Astar)算法的介绍可以参考另一篇博文: A*(Astar)搜索算法的实现(C语言)
1.问题描述
在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
2.问题可解性
八数码问题的一个状态实际上是0~9的一个排列,对于任意给定的初始状态和目标,不一定有解,也就是说从初始状态不一定能到达目标状态。因为排列有奇排列和偶排列两类,从奇排列不能转化成偶排列或相反。
如果一个数字0~8的随机排列871526340,用F(X)表示数字X前面比它小的数的个数,全部数字的F(X)之和为Y=∑(F(X)),如果Y为奇数则称原数字的排列是奇排列,如果Y为偶数则称原数字的排列是偶排列。
例如871526340这个排列的
Y=0+0+0+1+1+3+2+3+0=10
10是偶数,所以他偶排列。871625340
Y=0+0+0+1+1+2+2+3+0=9
9是奇数,所以他奇排列。
因此,可以在运行程序前检查初始状态和目标状态的窘是否相同,相同则问题可解,应当能搜索到路径。否则无解。
3.求解步骤
1)建立一个队列,计算初始结点的估价函数f,并将初始结点入队,设置队列头和尾指针。
2)取出队列头(队列头指针所指)的结点,如果该结点是目标结点,则输出路径,程序结束。否则对结点进行扩展。
3)检查扩展出的新结点是否与队列中的结点重复,若与不能再扩展的结点重复(位于队列头指针之前),则将它抛弃;若新结点与待扩展的结点重复(位于队列头指针之后),则比较两个结点的估价函数中g的大小,保留较小g值的结点。跳至第五步。
4)如果扩展出的新结点与队列中的结点不重复,则按照它的估价函数f大小将它插入队列中的头结点后待扩展结点的适当位置,使它们按从小到大的顺序排列,最后更新队列尾指针。
5)如果队列头的结点还可以扩展,直接返回第二步。否则将队列头指针指向下一结点,再返回第二步。
4.代码实现
二叉堆代码就不贴出来了,在另一篇博文
A*(Astar)搜索算法的实现(C语言) 中已经给出,在本文最后有资源下载的链接。可以拿到完整的代码
astar_8.h
/*
* author: Atom
* date: 2012/12/04
* file: astar_8.h
*/
#ifndef ASTAR_8_H
#define ASTAR_8_H
#include "bheap.h"
#define MALLOC(type,n) (type *)malloc((n)*sizeof(type))
#define MAX(a,b) ((a)>(b))?(a):(b)
#define UP -3
#define RIGHT 1
#define DOWN 3
#define LEFT -1
int node_distance[9][9];
struct step_node
{
int step_status[9];
int f;
int g;
int h;
struct step_node* parent;
};
int _comp(struct Bheap_node* n1, struct Bheap_node* n2);
int _eq(struct Bheap_node* n1, struct Bheap_node* n2);
void astar_8(int start[9], int end[9]);
static int calc_distance(int step_status[9], int end[9]);
static int arr_idx(int n, int step_status[9]);
static void init_node_distance(int node_distance[9][9]);
static int is_end(int step_status[9], int end[9]);
static int is_reachable(int space_idx, int flag);
static int move_space(int step_status[9], int flag);
static void print_step_status(int step_status[9]);
static int check_whether_reach(int start[9], int end[9]);
#endif
astar_8.c
/*
*