#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#define LIST_INIT_SIZE 80 // 线性表存储空间的初始化分配量
#define ok 1
#define error -1
typedef int ElemType;
typedef int Status;
Status InitSqList_Sq();
void actionway();
int InitAxing();
void location();
void actionway();
int LocationFatherX();
int LocationFatherY();
void extendNode();
Status EmptyList_Sq();
Status ListDelete();
Status ListInsert();
void preextendNode();
int equality();
Status ListDeletefromOPEN();
// 终态的位置
int L[3][3] = {1, 2, 3,
8, 0, 4,
7, 6, 5};
int p = 3; // 行数
int q = 3; // 列数
typedef struct Node
{
int action[4]; // 0的方向:上action[0] 下action[1] 左action[2] 右action[3]
//(值1:可以往当前方向前进;值0:不可以往当前方向前进)
struct Node *p; // 指向父节点的指针
int str[3][3]; // 保存当前状态信息的二维数组
int deep; // 深度
int cost // 位置错误的元素个数
} Node, *NodeList;
// 构造线性表
typedef struct SqList
{
Node *elem; // 存储空间基址(一维数组)
int length; // 当前的长度
int listsize; // 当前分配的存储容量(以sizeof(ElemType)为单位)
int tag; // 1为CLOSE表 0为OPEN表
} SqList;
SqList OPEN; // 构造OPEN表
SqList CLOSE; // 构造CLOSE表
int T = 1; // 记录
//线性表的初始化,构造一个空表
Status InitSqList_Sq(SqList *L)
{
L->elem = (Node *)malloc(LIST_INIT_SIZE * sizeof(Node));
if (!L->elem)
{
printf("存储空间分配失败\n");
return error;
}
L->length = 0;
L->listsize = LIST_INIT_SIZE;
printf("线性表初始化完成\n");
return ok;
}
// 线性表插入操作
Status ListInsert(SqList *L, Node *N)
{
int i, j, k;
int flag = 0; // 判断是否有通deep的节点插入到OPEN表中
//存储空间已满
if (L->length >= L->listsize)
return error;
// 插入到CLOSE表中
if (L->tag == 1)
{
L->elem[L->length] = *N;
L->length++;
}
// 插入到OPEN表中
if (L->tag == 0)
{
// 是空表则直接加入
if (EmptyList_Sq(L) == -1)
{
L->elem[L->length] = *N;
L->length++;
}
else
{
for (i = 0; i < L->length; i++)
{
if (L->elem[i].deep == N->deep)
{
flag = 1;
break;
}
}
if (flag == 1)
{ // 有相同deep的节点插入到OPEN表中
// 与前一个元素的cost+deep相等则加入
if ((N->cost + N->deep) == (L->elem[L->length - 1].cost + L->elem[L->length - 1].deep))
{
L->elem[L->length] = *N;
L->length++;
}
// cost+deep小于前一个
if ((N->cost + N->deep) < (L->elem[L->length - 1].cost + L->elem[L->length - 1].deep))
{
// 将相同deep的元素全部从OPEN表中移除
for (i = 0; i < L->length; i++)
{
if (L->elem[i].deep == N->deep)
{
ListDeletefromOPEN(&OPEN, i);
}
}
L->elem[L->length] = *N;
L->length++;
}
// cost + deep大于前一个什么都不执行
}else if (flag == 0)
{
// 没有相同deep的节点插入到OPEN表中
L->elem[L->length] = *N;
L->length++;
}
}
}
return ok;
}
// 线性表删除操作(固定取第一个元素)
Status ListDelete(SqList *L, int i, Node *N)
{
int j;
Node *q = &(L->elem[i]);
*N = *q;
for (j = i; j < L->length; j++)
{
L->elem[j] = L->elem[j + 1];
}
L->length--;
return ok;
}
// OPEN表的删除操作将某一次扩展的插入到OPEN表中的子节点全部删除
Status ListDeletefromOPEN(SqList *L, int i)
{
int j;
for (j = i; j < L->length; j++)
{
L->elem[j] = L->elem[L->length];
}
L->length = i;
return ok;
}
//判断线性表是否为空
Status EmptyList_Sq(SqList *L)
{
if (L->elem)
{
if (L->length != 0)
{
// printf("线性表不是空表\n");
return ok;
}
else
{
// printf("线性表是空表\n");
return error;
}
}
else
printf("线性表不存在\n");
return error;
}
// 构造初始态的八数码
int InitAxing(Node *N)
{
int i, j;
printf("请输入3*3的二维数组(数字0~8):\n");
for (i = 0; i < p; i++)
{
for (j = 0; j < q; j++)
{
scanf("%d", &N->str[i][j]);
}
}
// printf("\n");
// for(i = 0; i < p; i++){
// for(j = 0; j < q; j++){
// printf("%d ", &N->str[i][j]);
// }
// }
N->deep = 0;
// 求位置错误的元素个数
location(N);
// 确定可以扩展的节点
actionway(N);
}
// 求位置错误的元素个数
void location(Node *N)
{
int i, j;
int cost = 0;
for (i = 0; i < p; i++)
{
for (j = 0; j < q; j++)
{
if (N->str[i][j] != L[i][j])
{
cost++;
}
}
}
N->cost = cost;
}
// 判断0上下左右那些位置可以走
void actionway(Node *N)
{
int i, j;
int n, m; // 记录0的位置
for (i = 0; i < p; i++)
{
for (j = 0; j < q; j++)
{
if (N->str[i][j] == 0)
{
n = i;
m = j;
break;
}
}
}
// 能否向上
if (N->deep == 0 && n > 0 || n > 0 && (n - 1) != LocationFatherX(N->p))
{
N->action[0] = 1;
}
else
{
N->action[0] = 0;
}
// 能否向左
if (m > 0 && N->deep == 0 || m > 0 && (m - 1) != LocationFatherY(N->p))
{
N->action[2] = 1;
}
else
{
N->action[2] = 0;
}
// 能否向下
if (n < 2 && N->deep == 0 || n < 2 && (n + 1) != LocationFatherX(N->p))
{
N->action[1] = 1;
}
else
{
N->action[1] = 0;
}
// 能否向右
if (m < 2 && N->deep == 0 || m < 2 && (m + 1) != LocationFatherY(N->p))
{
N->action[3] = 1;
}
else
{
N->action[3] = 0;
}
}
// 找父节点0的行位置
int LocationFatherX(Node *N)
{
int i, j;
for (i = 0; i < p; i++)
for (j = 0; j < q; j++)
if (N->str[i][j] == 0)
return i;
}
// 找父节点0的列位置
int LocationFatherY(Node *N)
{
int i, j;
for (i = 0; i < p; i++)
for (j = 0; j < q; j++)
if (N->str[i][j] == 0)
return j;
}
// 扩展结点的方法
void extendNode(Node *N, int k)
{
int i, j;
int temp;
int n, m; // 父节点0元素的下标
Node childNode; // 创建子结点
int str[3][3];
childNode.p = N; // 子节点指向父节点
childNode.deep = childNode.p->deep + 1;
n = LocationFatherX(N);
m = LocationFatherY(N);
// 保存父节点的数组
for (i = 0; i < p; i++)
{
for (j = 0; j < q; j++)
{
str[i][j] = N->str[i][j];
}
}
// 0与上面的元素交换位置
if (k == 0)
{
temp = str[n - 1][m];
str[n - 1][m] = str[n][m];
str[n][m] = temp;
}
// 0与下面的元素交换位置
if (k == 1)
{
temp = str[n + 1][m];
str[n + 1][m] = str[n][m];
str[n][m] = temp;
}
// 0与左边的元素交换位置
if (k == 2)
{
temp = str[n][m - 1];
str[n][m - 1] = str[n][m];
str[n][m] = temp;
}
// 0与右边的元素交换位置
if (k == 3)
{
temp = str[n][m + 1];
str[n][m + 1] = str[n][m];
str[n][m] = temp;
}
// 创建子节点数组
for (i = 0; i < p; i++)
{
for (j = 0; j < q; j++)
{
childNode.str[i][j] = str[i][j];
}
}
// 判断子节点0可以往哪些方向移动
actionway(&childNode);
// 判断子节点位置错误的元素个数
location(&childNode);
for (i = 0; i < p; i++)
{
for (j = 0; j < q; j++)
{
printf("%d ", childNode.str[i][j]);
}
}
printf("cost:%d+deep:%d", childNode.cost, childNode.deep);
printf(" 可移动的方向:上%d,下%d,左%d,右%d\n", childNode.action[0], childNode.action[1], childNode.action[2], childNode.action[3]);
ListInsert(&OPEN, &childNode);
}
// 扩展结点
void preextendNode(Node *N)
{
int k = 0;
while (k <= 3)
{
if (N->action[k] == 1)
{
extendNode(N, k);
}
k++;
}
}
// 判断是否为目标结点
int equality(Node *N)
{
int i, j;
for (i = 0; i < p; i++)
{
for (j = 0; j < p; j++)
{
if (N->str[i][j] != L[i][j])
{
return error;
}
}
}
return ok;
}
int main()
{
int i, j, k;
InitSqList_Sq(&CLOSE);
CLOSE.tag = 1;
InitSqList_Sq(&OPEN);
OPEN.tag = 0;
printf("开始构造初始态:\n");
Node initialstate;
Node temp;
InitAxing(&initialstate);
// 将初始节点插入到OPEN表中
ListInsert(&OPEN, &initialstate);
// 扩展结点
while (EmptyList_Sq(&OPEN) != -1)
{
// 将第一个节点从OPEN表中删除,并用temp返回其值
ListDelete(&OPEN, 0, &temp);
// 将结点加入到CLOSE表中
ListInsert(&CLOSE, &temp);
if (equality(&temp) == 1)
{
printf("找到目标结点\n");
break;
}
else
{
preextendNode(&temp);
}
printf("\n");
printf("步骤%d:\n", T++);
printf("open表长度:%d\n", OPEN.length);
for (i = 0; i < OPEN.length; i++)
{
for (j = 0; j < p; j++)
{
for (k = 0; k < q; k++)
{
printf("%d ", OPEN.elem[i].str[j][k]);
}
}
printf("\n");
}
}
printf("\n");
printf("CLOSE表中走过的路径:\n");
for (i = 0; i < CLOSE.length; i++)
{
for (j = 0; j < p; j++)
{
for (k = 0; k < q; k++)
{
printf("%d ", CLOSE.elem[i].str[j][k]);
}
}
printf("\n");
}
return 0;
}
1:0的位置也算做了一次错误
2:没有判断无解的情况