@@ -514,7 +514,170 @@ main()
514514
515515### C
516516
517+ ``` C
518+ #include < stdio.h>
519+ #include < stdlib.h>
520+ #include < string.h>
521+
522+ // 定义一个结构体,表示棋盘上骑士的位置和相关的 A* 算法参数
523+ typedef struct {
524+ int x, y; // 骑士在棋盘上的坐标
525+ int g; // 从起点到当前节点的实际消耗
526+ int h; // 从当前节点到目标节点的估计消耗(启发式函数值)
527+ int f; // 总的估计消耗(f = g + h)
528+ } Knight;
529+
530+ #define MAX_HEAP_SIZE 2000000 // 假设优先队列的最大容量
531+
532+ // 定义一个优先队列,使用最小堆来实现 A* 算法中的 Open 列表
533+ typedef struct {
534+ Knight data[MAX_HEAP_SIZE];
535+ int size;
536+ } PriorityQueue;
537+
538+ // 初始化优先队列
539+ void initQueue (PriorityQueue * pq) {
540+ pq->size = 0;
541+ }
542+
543+ // 将骑士节点插入优先队列
544+ void push(PriorityQueue * pq, Knight k) {
545+ if (pq->size >= MAX_HEAP_SIZE) {
546+ // 堆已满,无法插入新节点
547+ return;
548+ }
549+ int i = pq->size++;
550+ pq->data[ i] = k;
551+ // 上滤操作,维护最小堆的性质,使得 f 值最小的节点在堆顶
552+ while (i > 0) {
553+ int parent = (i - 1) / 2;
554+ if (pq->data[ parent] .f <= pq->data[ i] .f) {
555+ break;
556+ }
557+ // 交换父节点和当前节点
558+ Knight temp = pq->data[ parent] ;
559+ pq->data[ parent] = pq->data[ i] ;
560+ pq->data[ i] = temp;
561+ i = parent;
562+ }
563+ }
564+
565+ // 从优先队列中弹出 f 值最小的骑士节点
566+ Knight pop(PriorityQueue * pq) {
567+ Knight min = pq->data[ 0] ;
568+ pq->size--;
569+ pq->data[ 0] = pq->data[ pq->size] ;
570+ // 下滤操作,维护最小堆的性质
571+ int i = 0;
572+ while (1) {
573+ int left = 2 * i + 1;
574+ int right = 2 * i + 2;
575+ int smallest = i;
576+ if (left < pq->size && pq->data[ left] .f < pq->data[ smallest] .f) {
577+ smallest = left;
578+ }
579+ if (right < pq->size && pq->data[ right] .f < pq->data[ smallest] .f) {
580+ smallest = right;
581+ }
582+ if (smallest == i) {
583+ break;
584+ }
585+ // 交换当前节点与最小子节点
586+ Knight temp = pq->data[ smallest] ;
587+ pq->data[ smallest] = pq->data[ i] ;
588+ pq->data[ i] = temp;
589+ i = smallest;
590+ }
591+ return min;
592+ }
593+
594+ // 判断优先队列是否为空
595+ int isEmpty(PriorityQueue * pq) {
596+ return pq->size == 0;
597+ }
598+
599+ // 启发式函数:计算从当前位置到目标位置的欧几里得距离的平方(避免开方,提高效率)
600+ int heuristic(int x, int y, int goal_x, int goal_y) {
601+ int dx = x - goal_x;
602+ int dy = y - goal_y;
603+ return dx * dx + dy * dy; // 欧几里得距离的平方
604+ }
605+
606+ // 用于记录从起点到棋盘上每个位置的最小移动次数
607+ int moves[ 1001] [ 1001 ] ;
608+
609+ // 骑士在棋盘上的8个可能移动方向
610+ int dir[ 8] [ 2 ] = {
611+ {-2, -1}, {-2, 1}, {-1, 2}, {1, 2},
612+ {2, 1}, {2, -1}, {1, -2}, {-1, -2}
613+ };
517614
615+ // 使用 A* 算法寻找从起点到目标点的最短路径
616+ int astar(int start_x, int start_y, int goal_x, int goal_y) {
617+ PriorityQueue pq;
618+ initQueue(&pq);
619+
620+ // 初始化 moves 数组,-1 表示未访问过的位置
621+ memset(moves, -1, sizeof(moves));
622+ moves[start_x][start_y] = 0; // 起点位置的移动次数为 0
623+
624+ // 初始化起始节点
625+ Knight start;
626+ start.x = start_x;
627+ start.y = start_y;
628+ start.g = 0;
629+ start.h = heuristic(start_x, start_y, goal_x, goal_y);
630+ start.f = start.g + start.h; // 总的估计消耗
631+
632+ push(&pq, start); // 将起始节点加入优先队列
633+
634+ while (!isEmpty(&pq)) {
635+ Knight current = pop(&pq); // 取出 f 值最小的节点
636+
637+ // 如果已经到达目标位置,返回所需的最小移动次数
638+ if (current.x == goal_x && current.y == goal_y) {
639+ return moves[current.x][current.y];
640+ }
641+
642+ // 遍历当前节点的所有可能移动方向
643+ for (int i = 0; i < 8; i++) {
644+ int nx = current.x + dir[i][0];
645+ int ny = current.y + dir[i][1];
646+
647+ // 检查新位置是否在棋盘范围内且未被访问过
648+ if (nx >= 1 && nx <= 1000 && ny >= 1 && ny <= 1000 && moves[nx][ny] == -1) {
649+ moves[nx][ny] = moves[current.x][current.y] + 1; // 更新移动次数
650+
651+ // 创建新节点,表示骑士移动到的新位置
652+ Knight neighbor;
653+ neighbor.x = nx;
654+ neighbor.y = ny;
655+ neighbor.g = current.g + 5; // 每次移动的消耗为 5(骑士移动的距离平方)
656+ neighbor.h = heuristic(nx, ny, goal_x, goal_y);
657+ neighbor.f = neighbor.g + neighbor.h;
658+
659+ push(&pq, neighbor); // 将新节点加入优先队列
660+ }
661+ }
662+ }
663+
664+ return -1; // 如果无法到达目标位置,返回 -1
665+ }
666+
667+ int main() {
668+ int n;
669+ scanf("%d", &n);
670+ while (n--) {
671+ int a1, a2, b1, b2; // 起点和目标点的坐标
672+ scanf("%d %d %d %d", &a1, &a2, &b1, &b2);
673+
674+ int result = astar(a1, a2, b1, b2); // 使用 A* 算法计算最短路径
675+ printf("%d\n", result); // 输出最小移动次数
676+ }
677+ return 0;
678+ }
679+
680+ ```
518681
519682
520683
0 commit comments