豆包MarsCode AI刷题心得4——82.理想火车站定位 | 豆包MarsCode AI刷题

52 阅读4分钟

题目分析

本题要求从给定的候选火车站位置中选择一个,使所有市民到火车站的曼哈顿距离总和最小。曼哈顿距离的定义为:两点 (x1,y1)(x_1, y_1)(x1​,y1​) 和 (x2,y2)(x_2, y_2)(x2​,y2​) 之间的距离为 ∣x1−x2∣+∣y1−y2∣|x_1 - x_2| + |y_1 - y_2|∣x1​−x2​∣+∣y1​−y2​∣。问题的核心是找到一个候选站点,使得与所有市民位置的距离总和最小。

这是一个典型的优化问题,我们需要遍历所有候选位置,计算其距离总和并找出最优解。


解题思路

  1. 输入与数据范围
    本题的输入包含两类点坐标:市民的位置和候选火车站的位置,分别有 NNN 和 MMM 个,且范围可以达到 100,000100,000100,000。需要在所有候选火车站中找到一个使总曼哈顿距离最小的站点。因为计算一次曼哈顿距离需要常数时间,暴力解法的时间复杂度是 O(N×M)O(N \times M)O(N×M),适合本题的数据范围。

  2. 暴力解法
    对每个候选站点 (pi,qi)(p_i, q_i)(pi​,qi​),计算其到所有市民位置的总距离。然后记录总距离最小的站点作为答案。如果有多个站点的总距离相同,则输出输入中第一个出现的站点。

    • 曼哈顿距离的性质非常简单,因此可以直接逐个累加所有市民到站点的距离。
    • 通过维护一个变量来记录当前的最小距离以及对应站点,可以在一次遍历后获得结果。
  3. 优化思路
    虽然暴力解法已经可以解决问题,但仍有优化空间。例如,当计算某个候选站点的总距离时,如果发现当前距离已经超过当前记录的最小距离,则可以提前终止后续计算(剪枝)。


实现步骤

  1. 读取输入
    解析市民和候选站点的坐标,分别存储在数组中。

  2. 暴力搜索
    对每个候选站点:

    • 初始化当前总距离为 0。
    • 遍历所有市民,计算其到该站点的曼哈顿距离并累加。
    • 与当前最小距离进行比较,更新最优解。
  3. 返回结果
    输出总距离最小的候选站点位置。


核心代码

以下是本题的 Java 实现:

java
Copy code
public class Main {
    public static int[] solution(int n, int m, int[][] citizens, int[][] locations) {
        long minDistance = Long.MAX_VALUE; // 当前最小总距离
        int[] bestLocation = new int[2];  // 最优站点

        for (int i = 0; i < m; i++) {
            int px = locations[i][0];
            int py = locations[i][1];
            long totalDistance = 0;

            for (int[] citizen : citizens) {
                int cx = citizen[0];
                int cy = citizen[1];
                totalDistance += Math.abs(cx - px) + Math.abs(cy - py);

                if (totalDistance >= minDistance) {
                    break; // 剪枝优化
                }
            }

            if (totalDistance < minDistance) {
                minDistance = totalDistance;
                bestLocation = locations[i];
            }
        }

        return bestLocation;
    }

    public static void main(String[] args) {
        int[][] citizens = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}};
        int[][] locations = {{3, 2}, {1, 0}, {0, 0}};

        int[] result = solution(4, 3, citizens, locations);
        System.out.println(result[0] + " " + result[1]); // 输出 1 0
    }
}

时间复杂度分析

  1. 外层循环遍历 MMM 个候选站点,内层循环遍历 NNN 个市民,总时间复杂度为 O(N×M)O(N \times M)O(N×M)。对于最大范围 N,M≤100,000N, M \leq 100,000N,M≤100,000,可以在合理时间内完成。
  2. 剪枝优化可以减少部分不必要的计算,在某些情况下加速程序运行。

测试与边界情况

  • 基本测试:包含少量市民和候选站点,验证程序功能是否正确。

  • 边界测试

    • N=1N = 1N=1,M=1M = 1M=1:市民和候选站点都只有一个。
    • 市民和候选站点重合。
    • 所有坐标的绝对值接近最大值(例如 −107-10^7−107 和 10710^7107)。
  • 性能测试

    • N,M=100,000N, M = 100,000N,M=100,000,验证程序在大输入范围下的执行效率。

总结

本题通过暴力搜索和剪枝优化,直接计算曼哈顿距离总和,解决了问题。由于问题本身具有简单的数学性质和数据范围合理,暴力方法已经足够高效。优化后的解法保证了正确性与运行效率的平衡,非常适合实际应用场景。