@@ -389,7 +389,170 @@ for _ in range(n):
389389
390390### C
391391
392+ ``` C
393+ #include < stdio.h>
394+ #include < stdlib.h>
395+ #include < string.h>
396+
397+ // 定义一个结构体,表示棋盘上骑士的位置和相关的 A* 算法参数
398+ typedef struct {
399+ int x, y; // 骑士在棋盘上的坐标
400+ int g; // 从起点到当前节点的实际消耗
401+ int h; // 从当前节点到目标节点的估计消耗(启发式函数值)
402+ int f; // 总的估计消耗(f = g + h)
403+ } Knight;
404+
405+ #define MAX_HEAP_SIZE 2000000 // 优先队列的最大容量
406+
407+ // 定义一个优先队列,使用最小堆来实现 A* 算法中的 Open 列表
408+ typedef struct {
409+ Knight data[MAX_HEAP_SIZE];
410+ int size;
411+ } PriorityQueue;
412+
413+ // 初始化优先队列
414+ void initQueue (PriorityQueue * pq) {
415+ pq->size = 0;
416+ }
417+
418+ // 将骑士节点插入优先队列
419+ void push(PriorityQueue * pq, Knight k) {
420+ if (pq->size >= MAX_HEAP_SIZE) {
421+ // 堆已满,无法插入新节点
422+ return;
423+ }
424+ int i = pq->size++;
425+ pq->data[ i] = k;
426+ // 上滤操作,维护最小堆的性质,使得 f 值最小的节点在堆顶
427+ while (i > 0) {
428+ int parent = (i - 1) / 2;
429+ if (pq->data[ parent] .f <= pq->data[ i] .f) {
430+ break;
431+ }
432+ // 交换父节点和当前节点
433+ Knight temp = pq->data[ parent] ;
434+ pq->data[ parent] = pq->data[ i] ;
435+ pq->data[ i] = temp;
436+ i = parent;
437+ }
438+ }
439+
440+ // 从优先队列中弹出 f 值最小的骑士节点
441+ Knight pop(PriorityQueue * pq) {
442+ Knight min = pq->data[ 0] ;
443+ pq->size--;
444+ pq->data[ 0] = pq->data[ pq->size] ;
445+ // 下滤操作,维护最小堆的性质
446+ int i = 0;
447+ while (1) {
448+ int left = 2 * i + 1;
449+ int right = 2 * i + 2;
450+ int smallest = i;
451+ if (left < pq->size && pq->data[ left] .f < pq->data[ smallest] .f) {
452+ smallest = left;
453+ }
454+ if (right < pq->size && pq->data[ right] .f < pq->data[ smallest] .f) {
455+ smallest = right;
456+ }
457+ if (smallest == i) {
458+ break;
459+ }
460+ // 交换当前节点与最小子节点
461+ Knight temp = pq->data[ smallest] ;
462+ pq->data[ smallest] = pq->data[ i] ;
463+ pq->data[ i] = temp;
464+ i = smallest;
465+ }
466+ return min;
467+ }
468+
469+ // 判断优先队列是否为空
470+ int isEmpty(PriorityQueue * pq) {
471+ return pq->size == 0;
472+ }
473+
474+ // 启发式函数:计算从当前位置到目标位置的欧几里得距离的平方(避免开方,提高效率)
475+ int heuristic(int x, int y, int goal_x, int goal_y) {
476+ int dx = x - goal_x;
477+ int dy = y - goal_y;
478+ return dx * dx + dy * dy; // 欧几里得距离的平方
479+ }
480+
481+ // 用于记录从起点到棋盘上每个位置的最小移动次数
482+ int moves[ 1001] [ 1001 ] ;
483+
484+ // 骑士在棋盘上的8个可能移动方向
485+ int dir[ 8] [ 2 ] = {
486+ {-2, -1}, {-2, 1}, {-1, 2}, {1, 2},
487+ {2, 1}, {2, -1}, {1, -2}, {-1, -2}
488+ };
489+
490+ // 使用 A* 算法寻找从起点到目标点的最短路径
491+ int astar(int start_x, int start_y, int goal_x, int goal_y) {
492+ PriorityQueue pq;
493+ initQueue(&pq);
494+
495+ // 初始化 moves 数组,-1 表示未访问过的位置
496+ memset(moves, -1, sizeof(moves));
497+ moves[start_x][start_y] = 0; // 起点位置的移动次数为 0
498+
499+ // 初始化起始节点
500+ Knight start;
501+ start.x = start_x;
502+ start.y = start_y;
503+ start.g = 0; // 从起点到起点的消耗为 0
504+ start.h = heuristic(start_x, start_y, goal_x, goal_y);
505+ start.f = start.g + start.h; // 总的估计消耗
506+
507+ push(&pq, start); // 将起始节点加入优先队列
508+
509+ while (!isEmpty(&pq)) {
510+ Knight current = pop(&pq); // 取出 f 值最小的节点
511+
512+ // 如果已经到达目标位置,返回所需的最小移动次数
513+ if (current.x == goal_x && current.y == goal_y) {
514+ return moves[current.x][current.y];
515+ }
516+
517+ // 遍历当前节点的所有可能移动方向
518+ for (int i = 0; i < 8; i++) {
519+ int nx = current.x + dir[i][0];
520+ int ny = current.y + dir[i][1];
521+
522+ // 检查新位置是否在棋盘范围内且未被访问过
523+ if (nx >= 1 && nx <= 1000 && ny >= 1 && ny <= 1000 && moves[nx][ny] == -1) {
524+ moves[nx][ny] = moves[current.x][current.y] + 1; // 更新移动次数
525+
526+ // 创建新节点,表示骑士移动到的新位置
527+ Knight neighbor;
528+ neighbor.x = nx;
529+ neighbor.y = ny;
530+ neighbor.g = current.g + 5; // 每次移动的消耗为 5(骑士移动的距离平方)
531+ neighbor.h = heuristic(nx, ny, goal_x, goal_y);
532+ neighbor.f = neighbor.g + neighbor.h;
533+
534+ push(&pq, neighbor); // 将新节点加入优先队列
535+ }
536+ }
537+ }
538+
539+ return -1; // 如果无法到达目标位置,返回 -1
540+ }
392541
542+ int main() {
543+ int n;
544+ scanf("%d", &n);
545+ while (n--) {
546+ int a1, a2, b1, b2; // 起点和目标点的坐标
547+ scanf("%d %d %d %d", &a1, &a2, &b1, &b2);
548+
549+ int result = astar(a1, a2, b1, b2); // 使用 A* 算法计算最短路径
550+ printf("%d\n", result); // 输出所需的最小移动次数
551+ }
552+ return 0;
553+ }
554+
555+ ```
393556
394557
395558
0 commit comments