A*算法(存在bug)

main
m7wzyfpis 2 years ago
parent 78a58bdd38
commit c3aeaedd22

288
A.cpp

@ -0,0 +1,288 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define N 3 // 定义拼图的维度这是一个3x3的拼图
typedef struct Node {
int puzzle[N][N]; // 存储拼图状态的数组
struct Node* parent; // 指向父节点的指针,用于追踪路径
int f, g, h; // A*算法中的 f, g, h 值
} Node;
// 创建新的拼图节点
Node* createNode(int puzzle[N][N]) {
Node* newnode = (Node*)malloc(sizeof(Node));
if (newnode == NULL) {
printf("Memory allocation failed!\n");
exit(0); // 如果内存分配失败,就退出程序
}
// 复制 puzzle 数组的值到新节点的 puzzle 数组中
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
newnode->puzzle[i][j] = puzzle[i][j];
}
}
newnode->parent = NULL; // 初始化 parent 节点为 NULL
newnode->f = 0; // 初始化 f 为 0
newnode->g = 0; // 初始化 g 为 0
newnode->h = 0; // 初始化 h 为 0
return newnode; // 返回指向新节点的指针
//请实现该函数
}
// 检查两个拼图状态是否相同
bool isSamePuzzle(int a[N][N], int b[N][N]) {
//相同则返回true,否则返回false
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (a[i][j] != b[i][j]) {
return false;
}
}
}
return true;
}
// 打印拼图状态
void printPuzzle(int puzzle[N][N]) {
//双重for循环实现拼图的打印
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
printf("%d ", puzzle[i][j]); // 打印每个元素,后面加一个空格
}
printf("\n");
}
// 启发函数,计算当前状态到目标状态的估计代价
int heuristic(Node* current, Node* goal) {
int h = 0;
// 计算不匹配的拼图块数量
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (current->puzzle[i][j] != goal->puzzle[i][j]) {
h++; // 如果当前节点和目标节点的拼图块不匹配,那么 h 就加 1
}
}
}
return h; // 返回 h 的值,即不匹配的拼图块的数量
}
// 移动操作,生成新的拼图状态
Node* move(Node* current, int dir) {
int key_x, key_y;//记录空白块的位置
// 找到空白块的位置
int new_x, new_y; // 用于记录移动后的空白块的位置
int temp; // 用于交换拼图块的值
bool isValidMove = false; // 标志位,表示移动是否有效
// 遍历 current 节点的 puzzle 数组,找到值为 0 的拼图块
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (current->puzzle[i][j] == 0) {
key_x = i;
key_y = j;
isValidMove = true; // 表示找到了空白块,移动是可能的
break;
}
}
if (isValidMove) break;
}
// 如果没有找到空白块,返回 NULL
if (!isValidMove) return NULL;
new_x = key_x;
new_y = key_y;
// 根据移动方向更新 new_x 和 new_y 的值
if (dir == 0 && new_x > 0) new_x--; // 上移
else if (dir == 1 && new_x < N-1) new_x++; // 下移
else if (dir == 2 && new_y > 0) new_y--; // 左移
else if (dir == 3 && new_y < N-1) new_y++; // 右移
else return NULL; // 如果新位置超出边界,返回 NULL
// 创建新节点,复制当前拼图状态,并交换块的位置
Node* newNode = (Node*)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Memory allocation failed!\n");
exit(0); // 如果内存分配失败,就退出程序
}
//给new_x、new_y赋值
// 根据移动方向更新新块的位置,上下左右移动
// 检查新位置是否在边界内
// 创建新节点,复制当前拼图状态,并交换块的位置
Node* new_node = createNode(current->puzzle);
new_node->puzzle[key_x][key_y] = current->puzzle[new_x][new_y];
new_node->puzzle[new_x][new_y] = 0;
return new_node;
}
// A*算法,寻找最短路径
Node* AStar(Node* start, Node* goal) {
Node* OPEN[1000]; // 开放列表,用于存储待探索的节点
Node* CLOSED[1000]; // 关闭列表,用于存储已探索的节点
int OPEN_SIZE = 0; // 开放列表的大小
int CLOSED_SIZE = 0; // 关闭列表的大小
OPEN[0] = start; // 将起始节点添加到开放列表
OPEN_SIZE = 1; // 开放列表的大小设置为1
CLOSED_SIZE = 0; // 关闭列表的大小设置为0
while (OPEN_SIZE > 0) {//对open列表进行操作
int min_f = OPEN[0]->f;//初始化最小的f
int min_index = 0;
// 查找开放列表中具有最小f值的节点
for (int i = 0; i < OPEN_SIZE; i++) {
if (OPEN[i]->f < min_f) {
min_f = OPEN[i]->f;
min_index = OPEN[i];
}
}
Node* current = OPEN[min_index]; // 获取具有最小f值的节点
// 如果当前节点与目标状态匹配,表示找到解
if (isSamePuzzle(minNode, goal)) {
return min_index;
}
//开放列表的大小减1表示从开放列表中移除了一个节点
//将最小 f 值的节点移到开放列表的末尾,以便稍后将其添加到关闭列表中。
for (int i = 0; i < OPEN_SIZE; i++) {
if (OPEN[i] == minNode) {
for (int j = i; j < OPEN_SIZE - 1; j++) {
OPEN[j] = OPEN[j+1];
}
OPEN_SIZE--;
break;
}
}
//这是为了优化开放列表的结构。
Node* temp = OPEN[min_index];
OPEN[min_index] = OPEN[OPEN_SIZE];
OPEN[OPEN_SIZE] = temp;
//将当前节点添加到关闭列表关闭列表大小加1
CLOSED[CLOSED_SIZE] = *current;
CLOSED_SIZE++;
int key = 0;
// 查找当前节点中空白块的位置
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (current->puzzle[i][j] == 0) {
key = i * N + j;
break;
}
}
}
// 尝试四个方向的移动操作
for (int dir = 0; dir < 4; dir++) {
Node* new_node = move(current, dir);
if (new_node != NULL && !isSamePuzzle(new_node->puzzle, current->puzzle)) {
//得到对应的g、f、h值
int gNew = current->g + 1;
int hNew = heuristic(new_node, goal);
int fNew = gNew + hNew;
bool in_OPEN = false;
int open_index = -1;
// 检查新节点是否在开放列表中
for (int i = 0; i < OPEN_SIZE; i++) {
if (isSamePuzzle(new_node->puzzle, OPEN[i]->puzzle)) {
in_OPEN = true;
open_index = i;
break;
}
}
bool in_CLOSED = false;
// 检查新节点是否在关闭列表中
for (int i = 0; i < CLOSED_SIZE; i++) {
if (isSamePuzzle(new_node->puzzle, CLOSED[i]->puzzle)) {
in_CLOSED = true;
break;
}
}
//若该节点机不在开放列表中也不在关闭列表中
if (!in_OPEN && !in_CLOSED) {
//把gNew、hNew、fNew赋给new_nod对应的g、h、f值并将其父节点设置为当前节点。
new_node->g = gNew;
new_node->h = hNew;
new_node->f = fNew;
new_node->parent = current;
// 添加新节点new_node到开放列表开放列表大小加1
OPEN[OPEN_SIZE] = new_node; // 添加新节点到开放列表中
OPEN_SIZE++; // 开放列表大小加1
}
//如果新节点已经在开放列表中,但新的 f 值更小,将更新开放列表中已存在节点的信息。
else if (in_OPEN && fNew < OPEN[open_index]->f) {
OPEN[open_index]->parent = current;
OPEN[open_index]->f = fNew;
}
}
}
}
return NULL; // 无解
}
// 打印解路径
void printPath(Node* final) {
if (final == NULL) {
return;
}
printPath(final->parent); // 递归打印路径
for (int i = 0; i < N; i++) {
if (i%3==0){
printf("-------\n");
}
for (int j = 0; j < N; j++) {
printf("%d ", final->puzzle[i][j]);
}
printf("\n");
}
}
int main() {
//int start[N][N] = {{2, 0, 3}, {1, 8, 4}, {7, 6, 5}};
//int target[N][N] = {{1, 2, 3}, {8, 0, 4}, {7, 6, 5}};
// int start[N][N] = {{2, 8, 3}, {1, 6, 4}, {7, 0, 5}};
// int target[N][N] = {{1, 2, 3}, {8, 0, 4}, {7, 6, 5}};
int start[N][N] = {{2, 8, 3}, {1, 0, 4}, {7, 6, 5}};
int target[N][N] = {{1, 2, 3}, {8, 0, 4}, {7, 6, 5}};
Node* init = createNode(start);
Node* goal = createNode(target);
Node* final = AStar(init, goal);
if (final) {
printf("This problem has a solution:\n");
printPath(final); // 打印解路径
} else {
printf("This problem has no solution\n");
}
return 0;
}
Loading…
Cancel
Save