题目解析:国际象棋跳跃问题 | 豆包MarsCode AI 刷题

106 阅读3分钟

豆包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


解题思路

  1. 这类问题优先考虑搜索解题,这里我使用的是BFS
  2. 用 BFS 来遍历每个可达的点,每个点存储当前的坐标以及到该点的步数。
  3. 对于每个点,我们先考虑马的跳跃(8种可能的移动),然后再考虑象的跳跃(虽然国际象棋中象在斜线上可以随意移动,但是要限制计算范围)。
  4. 使用一个 visited 集合来避免重复访问同一个点,确保每个点只被访问一次。
  5. 如果在某一步中到达了目标点 (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
    }
}

代码解释

  1. 初始化

    • knightMoves 数组提前存储马的所有可能的跳跃,方便后边遍历。
    • queue 存储当前坐标和步数。
    • visited 记录已访问的坐标,避免重复访问。
  2. BFS 遍历

    • 对于每一个坐标,首先考虑马的跳跃(8种可能的跳跃),然后考虑象的跳跃(注意题目是国际象棋,象能跳多步,之前一直用象飞田,后面才发现问题)。
    • 如果某一跳跃能到达目标坐标 (x2, y2),返回当前的步数 steps + 1

复杂度分析

  • 时间复杂度:BFS 遍历每个可能的坐标,最坏情况下,复杂度为 O(V + E),其中 V 是访问的点的数量,E 是每个点的邻接点。
  • 空间复杂度:主要由队列和访问集合占用的空间组成,O(V)。