解题报告: 理想火车站定位 | 豆包MarsCode AI刷题

37 阅读5分钟

问题描述

A市市长小F计划在市区内选址新建一个火车站,以便市民的日常出行。市区布局呈规则网格状,两个位置[x1, y1][x2, y2]之间的距离采用曼哈顿距离计算:|x1 - x2| + |y1 - y2|

市政府已初步选定了M个备选地址,现需要确定一个位置作为火车站选址,目标是使所有市民到火车站的总曼哈顿距离最短。如果存在多个符合条件的选址,则选择最先出现的地址。

输入数据包括:

  • N:市民总人数;
  • M:备选火车站位置数;
  • citizens:市民居住位置列表,每个元素为一个坐标[x_i, y_i]
  • locations:备选火车站位置列表,每个元素为一个坐标[p_i, q_i]

输出:最优火车站位置[p, q]


示例输入与输出

示例1

输入:

n = 4
m = 3
citizens = [[-1, -1], [-1, 1], [1, -1], [1, 1]]
locations = [[3, 2], [1, 0], [0, 0]]

输出:

[1, 0]

示例2

输入:

n = 2
m = 2
citizens = [[0, 0], [0, 4]]
locations = [[0, 2], [0, 3]]

输出:

[0, 2]

示例3

输入:

n = 3
m = 1
citizens = [[10, 10], [20, 20], [30, 30]]
locations = [[15, 15]]

输出:

[15, 15]

解题思路

曼哈顿距离的分解

曼哈顿距离可以分解为两个维度的绝对差值之和:

d(x1,y1,x2,y2)=∣x1−x2∣+∣y1−y2∣d(x1, y1, x2, y2) = |x1 - x2| + |y1 - y2|

因此,总距离的计算可以分别在x轴和y轴维度独立进行。通过这种分解,问题规模大大简化。


优化策略

  1. 前缀和优化绝对差值计算

    • 将所有市民的x坐标排序后,利用前缀和快速计算任意位置到所有市民的距离之和。类似地处理y坐标。
    • 排序和前缀和的构建时间复杂度为O(N log N)
  2. 二分查找

    • 对每个候选位置的xy坐标,利用二分查找确定该坐标在市民坐标排序数组中的位置。
    • 结合前缀和,快速求解两侧距离。
  3. 逐一评估候选位置

    • 计算所有候选位置的总距离,记录最小值。如果存在多个最优解,选择最早出现的候选位置。

实现步骤

  1. 分离并排序坐标

    • 将市民的xy坐标分别提取,排序后构建前缀和数组。
  2. 计算候选位置距离

    • 对每个候选位置,分别计算xy方向的总距离,最后求和。
  3. 记录最优结果

    • 通过遍历候选位置,找到最小总距离的候选位置,并输出其坐标。

算法实现

以下是基于 Python 的代码实现:

import bisect

def find_optimal_station(N, M, citizens, candidates):
    # 提取并排序市民的x和y坐标
    x_coords = [x[0] for x in citizens]
    y_coords = [x[1] for x in citizens]
    x_sorted = sorted(x_coords)
    y_sorted = sorted(y_coords)
    
    # 构建前缀和
    prefix_x = [0] * (N + 1)
    prefix_y = [0] * (N + 1)
    for i in range(N):
        prefix_x[i + 1] = prefix_x[i] + x_sorted[i]
        prefix_y[i + 1] = prefix_y[i] + y_sorted[i]
    
    # 遍历候选位置
    min_distance = float('inf')
    best_location = None
    for p, q in candidates:
        # 计算x方向总距离
        pos_x = bisect.bisect_left(x_sorted, p)
        dist_x = p * pos_x - prefix_x[pos_x] + (prefix_x[N] - prefix_x[pos_x]) - p * (N - pos_x)
        # 计算y方向总距离
        pos_y = bisect.bisect_left(y_sorted, q)
        dist_y = q * pos_y - prefix_y[pos_y] + (prefix_y[N] - prefix_y[pos_y]) - q * (N - pos_y)
        # 总距离
        total_distance = dist_x + dist_y
        
        # 更新最优解
        if total_distance < min_distance:
            min_distance = total_distance
            best_location = (p, q)
    
    return list(best_location)

# 测试样例
if __name__ == "__main__":
    citizens1 = [[-1, -1], [-1, 1], [1, -1], [1, 1]]
    locations1 = [[3, 2], [1, 0], [0, 0]]
    print(find_optimal_station(4, 3, citizens1, locations1))  # 输出:[1, 0]

时间复杂度分析

  1. 排序和前缀和构建
    • 排序市民坐标数组的时间复杂度为O(N log N)
    • 构建前缀和的时间复杂度为O(N)
  2. 候选位置评估
    • 对每个候选位置,在xy两个维度上分别进行二分查找,时间复杂度为O(log N)
    • 因此,总时间复杂度为O(M log N)

总时间复杂度为:

O(Nlog⁡N+Mlog⁡N)O(N \log N + M \log N)


空间复杂度分析

  • 主要使用了排序后的坐标数组和前缀和数组,总空间复杂度为O(N)

测试样例设计

  1. 常规情况:市民和候选位置分布在网格内,验证算法的正确性。
  2. 边界情况
    • N = 1M = 1:只有一个市民或一个候选位置。
    • 市民和候选位置重叠:验证算法能否正确返回。
  3. 极端情况
    • 所有市民的坐标相同。
    • 市民数量或候选位置数量达到最大值(如100,000)。

总结与反思

  1. 算法优势

    • 分解曼哈顿距离简化了问题复杂度。
    • 使用排序、前缀和和二分查找高效处理大规模数据。
  2. 改进方向

    • 如果候选位置过多,可以结合分块优化进一步减少计算开销。
    • 在实际场景中,可能需要考虑更复杂的约束条件(如地形、成本等)。
  3. 拓展思考

    • 曼哈顿距离的优化策略可应用于类似问题,如配送中心选址、网格路径规划等场景。
    • 可尝试拓展到三维坐标或其他距离度量方式(如欧几里得距离)。