@@ -514,7 +514,170 @@ main()
514
514
515
515
### C
516
516
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
+ };
517
614
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
+ ```
518
681
519
682
520
683
0 commit comments