一、题目分析
题目给定了一个城市网格状的街道布局,并要求我们选择一个最优的火车站位置。选择的标准是使得所有市民到达火车站的曼哈顿距离总和最小。曼哈顿距离是一种在平面上计算两点之间的“直线”距离,计算方式为两个点的横坐标差的绝对值加上纵坐标差的绝对值。 输入:
n:市民的数量。m:备选的火车站位置数量。citizens:每个市民的位置,列表中的每一项为一个坐标[x, y]。locations:每个备选的火车站位置,列表中的每一项为一个坐标[p, q]。
输出:
- 选择的最优火车站位置,返回一个坐标
[p, q]。
我们需要在所有备选火车站中选出一个,使得从所有市民到该火车站的曼哈顿距离之和最小。如果有多个火车站距离相同,则选择第一个出现的火车站。
二、解题思路
-
曼哈顿距离:这是一个经典的距离计算方法,适用于网格状的城市布局。在这个问题中,我们需要计算从每个市民到每个备选火车站的曼哈顿距离,并求出每个火车站位置的总距离。
-
暴力枚举:由于没有明显的优化方法,我们可以直接遍历所有的火车站位置,计算所有市民到该火车站的距离总和,最终选择距离最小的那个。
-
贪心思想:这个问题符合贪心算法的基本思想——每次做出局部最优选择。我们每次选择一个备选火车站,计算该站与所有市民的总曼哈顿距离,选择总距离最小的火车站作为最终答案。
-
边界条件:
- 只有一个备选火车站时,直接选择该火车站。
- 所有市民在同一位置时,任何火车站都会有相同的距离。
三、函数实现
def solution(n, m, citizens, locations): # 初始化最小总距离为无穷大 min_distance = float('inf') best_location = None
# 遍历所有备选位置
for location in locations:
total_distance = 0
# 计算该火车站到所有市民的曼哈顿距离总和
for citizen in citizens:
total_distance += abs(location[0] - citizen[0]) + abs(location[1] - citizen[1])
# 如果当前火车站的总距离更小,更新最优火车站
if total_distance < min_distance:
min_distance = total_distance
best_location = location
return best_location
解释:
- 初始化:首先我们用
min_distance来记录最小的曼哈顿距离总和,初始值为正无穷大。best_location用来存储当前最优的火车站位置。 - 遍历备选位置:我们遍历所有备选的火车站
locations,对每个火车站位置计算所有市民到该火车站的曼哈顿距离之和。 - 更新最优位置:如果某个火车站的总距离小于当前的最小距离,我们就更新
min_distance和best_location。 - 返回最优解:遍历完成后,我们返回距离总和最小的火车站位置。
四、个人收获与感悟
-
贪心算法的运用: 通过这道题,我加深了对贪心算法的理解。贪心算法的核心思想是在每一步做出局部最优的选择,从而求解全局最优解。虽然本题的解决方案采用的是暴力遍历的方式,但从算法的角度来看,每次计算一个火车站的总曼哈顿距离时,实际上是在做局部最优选择,即选择最小的距离。因此,这道题也是一个典型的贪心问题。
-
曼哈顿距离的应用: 本题使用曼哈顿距离来计算两点之间的距离,适用于城市网格结构中需要按照轴对齐移动的情况。这个概念在实际问题中非常有用,尤其是在城市规划和导航系统中。
-
暴力解法的适用性: 尽管这个问题使用了暴力解法(逐一计算每个备选火车站的总距离),但对于中小规模的数据集,这种方法是可行的。通过遍历所有备选位置并计算总距离,虽然时间复杂度为 O(m * n),但问题本身并不复杂,暴力解法足以解决。
-
代码简洁性和可读性: 通过这道题,我学会了如何将问题的本质提炼成简单清晰的代码。在写完代码之后,能够直观地理解其意义和每一步的操作,这让我体会到编程中简洁性和可读性的必要性。
五、总结
这道题通过对曼哈顿距离的应用,帮助我理解了贪心算法和暴力枚举法的结合应用。尽管题目要求我们计算所有备选火车站的总距离,最终选择最优解,但这种遍历所有可能解的方式在问题规模不大时是非常高效且易于实现的。同时,这个问题也让我更熟悉了如何处理坐标和距离相关的算法问题,具有很好的实践意义。