题目描述

给定 C×RC \times R 网格(C,R⩽150C,R \leqslant 150),格子为 .、*、K、H。棋子从 K 出发按马走日跳跃,落点需在网格内且非障碍,求到 H 的最少跳跃次数。数据保证有解。

输入:C RC\ R,随后 RR 行地图。
输出:最少跳跃次数。

思路

把非障碍格看成顶点、马步看成边权为 1 的边,得到无权无向图。BFS 按层扩展,dist 数组兼作判重,首次到达 H 时的层数就是答案。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-01 15:14
# update_at: 2026-10-01 15:14

import sys
from collections import deque
from collections.abc import Iterator

START, GOAL, OBSTACLE = b"KH*"  # 三个标记的 ASCII 码;bytes 解包出来的就是整数

JUMPS = (  # 马走日的 8 个位移,一行一个家族:先 |dr|=1 的 4 个,再 |dr|=2 的 4 个
    (1, 2), (1, -2), (-1, 2), (-1, -2),
    (2, 1), (2, -1), (-2, 1), (-2, -1),
)


def neighbours(pos: int, board: bytes, C: int, R: int) -> Iterator[int]:
    """产出 pos 一次跳跃能落到的可行下标:越界或踩到障碍的候选直接不产出。"""
    row, col = divmod(pos, C)
    for dr, dc in JUMPS:
        r, c = row + dr, col + dc
        if 0 <= r < R and 0 <= c < C and board[r * C + c] != OBSTACLE:
            yield r * C + c


def bfs(board: bytes, C: int, R: int, start: int) -> int:
    """逐层扩展求最短路,返回首次够到草的层数;题目保证有解,故不会走空。"""
    dist = [-1] * (C * R)  # -1 同时表示"没访问过"和"距离未知"
    dist[start] = 0
    q = deque([start])
    while q:
        pos = q.popleft()
        for nxt in neighbours(pos, board, C, R):
            if dist[nxt] < 0:
                if board[nxt] == GOAL:  # 先进先出,第一次够到就是最少跳跃次数
                    return dist[pos] + 1
                dist[nxt] = dist[pos] + 1
                q.append(nxt)
    return -1


def solve() -> None:
    data = iter(sys.stdin.buffer.read().split())
    C, R = int(next(data)), int(next(data))
    board = b"".join(next(data) for _ in range(R))  # 逐行拼接,下标即 r * C + c

    print(bfs(board, C, R, board.index(START)))


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-06 11:40
 * update_at: 2026-10-06 11:40
 */

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

typedef long long ll;

const int MAXC = 155;
const int MAXR = 155;

int C, R;
char g[MAXR][MAXC]; // 网格地图
int dist[MAXR][MAXC]; // dist[r][c] = 从起点到(r,c)的最少跳跃次数,-1表示未访问

// 马走日的8个位移
const int dr[8] = { 1, 1, -1, -1, 2, 2, -2, -2 };
const int dc[8] = { 2, -2, 2, -2, 1, -1, 1, -1 };

struct Node {
    int r, c;
};

queue<Node> q;

int bfs(int sr, int sc) {
    memset(dist, -1, sizeof(dist));
    dist[sr][sc] = 0;
    q.push((Node){sr, sc});
    while (!q.empty()) {
        Node u = q.front();
        q.pop();
        for (int i = 0; i < 8; i++) {
            int nr = u.r + dr[i];
            int nc = u.c + dc[i];
            if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue; // 越界
            if (g[nr][nc] == '*') continue; // 障碍
            if (dist[nr][nc] != -1) continue; // 已访问
            if (g[nr][nc] == 'H') // 首次到达草地即最少跳跃次数
                return dist[u.r][u.c] + 1;
            dist[nr][nc] = dist[u.r][u.c] + 1;
            q.push((Node){nr, nc});
        }
    }
    return -1; // 题目保证有解,不会走到这里
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> C >> R;
    int sr = -1, sc = -1;
    for (int i = 0; i < R; i++) {
        cin >> g[i];
        for (int j = 0; j < C; j++) {
            if (g[i][j] == 'K') {
                sr = i;
                sc = j;
            }
        }
    }
    cout << bfs(sr, sc) << "\n";
    return 0;
}