豆包MarsCode138 国际象棋跳跃问题
问题描述
小U掌握了国际象棋中“象”和“马”的跳跃能力,在一个无限大的平面直角坐标系中,她每一步可以模仿象和马的跳跃方式移动。在每次询问中,小U需要计算从初始坐标 (x1,y1) 到 (x2,y2) 所需的最少步数。
- 象的跳跃:可以移动到 (x+k,y+k) 或 (x+k,y−k),其中 k 是任意整数。
- 马的跳跃:可以移动到 (x+a,y+b),其中 ∣a∣+∣b∣=3 且 1≤∣a∣,∣b∣≤2。
你需要在每次询问中计算从起点到终点所需的最少步数。
问题链接:国际象棋跳跃问题 - MarsCode
测试样例
样例1:
输入:
x1 = 0, y1 = 0, x2 = 1, y2 = 1
输出:1
样例2:
输入:
x1 = 0, y1 = 0, x2 = 2, y2 = 1
输出:1
样例3:
输入:
x1 = 0, y1 = 0, x2 = 3, y2 = 3
输出:1
样例4:
输入:
x1 = -3, y1 = -2, x2 = 2, y2 = 1
输出:2
解题思路
- 这类问题优先考虑搜索解题,这里我使用的是BFS
- 用 BFS 来遍历每个可达的点,每个点存储当前的坐标以及到该点的步数。
- 对于每个点,我们先考虑马的跳跃(8种可能的移动),然后再考虑象的跳跃(虽然国际象棋中象在斜线上可以随意移动,但是要限制计算范围)。
- 使用一个
visited集合来避免重复访问同一个点,确保每个点只被访问一次。 - 如果在某一步中到达了目标点
(x2, y2),则返回当前的步数。
代码实现
import java.util.*;
public class Main {
public static int solution(int x1, int y1, int x2, int y2) {
if (x1 == x2 && y1 == y2) {
return 0; // 如果起始和目标坐标相同,步数为0
}
// 马的移动
int[][] knightMoves = {
{ 2, 1 }, { 2, -1 }, { -2, 1 }, { -2, -1 },
{ 1, 2 }, { 1, -2 }, { -1, 2 }, { -1, -2 }
};
// BFS初始化
Queue<int[]> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
queue.add(new int[] { x1, y1, 0 }); // {x, y, steps}
visited.add(x1 + "," + y1);
while (!queue.isEmpty()) {
int[] curr = queue.poll();
int x = curr[0], y = curr[1], steps = curr[2];
// 检查所有马的移动
for (int[] move : knightMoves) {
int nx = x + move[0];
int ny = y + move[1];
if (nx == x2 && ny == y2) {
return steps + 1;
}
String key = nx + "," + ny;
if (!visited.contains(key)) {
visited.add(key);
queue.add(new int[] { nx, ny, steps + 1 });
}
}
// 检查象的多步移动
for (int k = 1; k <= Math.max(Math.abs(x2 - x1), Math.abs(y2 - y1)); k++) {
for (int[] move : new int[][] { { k, k }, { k, -k }, { -k, k }, { -k, -k } }) {
int nx = x + move[0];
int ny = y + move[1];
if (nx == x2 && ny == y2) {
return steps + 1;
}
String key = nx + "," + ny;
if (!visited.contains(key)) {
visited.add(key);
queue.add(new int[] { nx, ny, steps + 1 });
}
}
}
}
return -1;
}
public static void main(String[] args) {
System.out.println(solution(0, 0, 1, 1) == 1); // 1
System.out.println(solution(0, 0, 2, 1) == 1); // 1
System.out.println(solution(0, 0, 3, 3) == 1); // 1
System.out.println(solution(-3, -2, 2, 1) == 2); // 2
}
}
代码解释
-
初始化:
knightMoves数组提前存储马的所有可能的跳跃,方便后边遍历。queue存储当前坐标和步数。visited记录已访问的坐标,避免重复访问。
-
BFS 遍历:
- 对于每一个坐标,首先考虑马的跳跃(8种可能的跳跃),然后考虑象的跳跃(注意题目是国际象棋,象能跳多步,之前一直用象飞田,后面才发现问题)。
- 如果某一跳跃能到达目标坐标
(x2, y2),返回当前的步数steps + 1。
复杂度分析
- 时间复杂度:BFS 遍历每个可能的坐标,最坏情况下,复杂度为 O(V + E),其中 V 是访问的点的数量,E 是每个点的邻接点。
- 空间复杂度:主要由队列和访问集合占用的空间组成,O(V)。