题目链接 P1443 马的遍历 - 洛谷
解题思路:
这道题目要求计算马从初始位置 (x1, y1) 出发,到达棋盘 n × m 上每个点的最少步数。解题的核心思路是 广度优先搜索(BFS),因为 BFS 天然适合在无权图中寻找最短路径(最少步数)。具体步骤如下:
1. 问题建模:
- 将棋盘视为一个图,每个格子是一个节点。
- 如果马可以从一个格子移动到另一个格子(按“日”字走法),则两个节点之间存在一条边。
-“日”字走法:就是传统象棋中的马走日,所以定义八个方向:
static int[] dx = {2, 2, 1, 1, -1, -1, -2, -2};
static int[] dy = {1, -1, 2, -2, 2, -2, 1, -1};
2. BFS 算法:
- 使用队列存储待访问的节点。
- 从初始位置 (x1, y1) 开始,步数为 0。
- 每次从队列取出一个节点,遍历其所有可能的移动方向(8 个方向)。
- 对于每个新位置:
- 检查是否在棋盘范围内。
- 如果未被访问过(即 `dis[a][b] == -1),则更新步数并加入队列。
3. 输出结果:
- dis[i][j] 存储从 (x1, y1) 到 (i, j) 的最少步数。
- 若 dis[i][j] == -1,表示该点不可达。
关键点
- BFS 保证最短路径**:由于 BFS 是逐层扩展的,首次访问某个节点时的步数一定是最小的。
- 方向数组**:使用 `dx` 和 `dy` 数组表示马的 8 种移动方式。
- 初始化与边界处理**:
- dis 初始化为 -1,表示未访问。
- 每次移动后检查是否越界。
时间复杂度
- O(n × m):每个节点最多入队一次,每次处理 8 个方向,总复杂度为线性。
AC代码:
import java.util.*;
public class Main {
static int n; // 棋盘的行数
static int m; // 棋盘的列数
static int x1; // 马的初始行坐标
static int y1; // 马的初始列坐标
static int[][] dis = new int[410][410]; // 存储每个位置的最少步数
// 马可以移动的8个方向("日"字走法)
static int[] dx = {2, 2, 1, 1, -1, -1, -2, -2};
static int[] dy = {1, -1, 2, -2, 2, -2, 1, -1};
static Queue<int[]> q = new LinkedList<>(); // BFS队列,存储待处理的坐标
// BFS函数,计算从(x1, y1)出发到所有点的最少步数
public static void bfs(int x1, int y1) {
// 初始化距离数组为-1,表示未访问
for (int[] di : dis) {
Arrays.fill(di, -1);
}
q.clear(); // 清空队列(虽然本题只需调用一次,但养成好习惯)
q.add(new int[]{x1, y1}); // 起点入队
dis[x1][y1] = 0; // 起点到自己的距离为0
while (!q.isEmpty()) {
int[] t = q.poll(); // 取出队首坐标
for (int i = 0; i < 8; i++) { // 遍历8个方向
int a = t[0] + dx[i]; // 计算新坐标
int b = t[1] + dy[i];
// 越界检查
if (a < 1 || a > n || b < 1 || b > m) continue;
// 已访问过的点跳过
if (dis[a][b] != -1) continue;
// 更新新坐标的步数
dis[a][b] = dis[t[0]][t[1]] + 1;
// 新坐标入队
q.add(new int[]{a, b});
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt(); // 输入行数
m = sc.nextInt(); // 输入列数
x1 = sc.nextInt(); // 输入初始行坐标
y1 = sc.nextInt(); // 输入初始列坐标
bfs(x1, y1); // 执行BFS计算最少步数
// 输出结果
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
System.out.printf("%-5d", dis[i][j]); // 格式化输出,左对齐占5位
}
System.out.println(); // 换行
}
}
}