假设输入被指定为建筑物对象的数组,其中每个建筑物都有一些居民与街道起点的距离。
总距离= SUM(距离[i] * #residents [i])
我在这里发现两个相似的问题,但它们的要求略有不同:
Minimizing weighted sum:此问题的解决方案找到了跨越所有点的最小路径。在这里,我正在寻找从每个建筑物到邮箱所在地的最小总距离。
Minimum Total Distance From Locations:它使用2D坐标,更重要的是,解决方案不考虑每个位置的重量(居民数量)。
我在阅读编程访谈元素时看到了这个问题(非常好的书,BTW),这被列为quickselect算法的变体。考虑到中位数是最小化距离总和的点,看起来解决方案将涉及快速选择在O(N)中找到街道“中间”位置的建筑物。
但我无法弄清楚如何计算每栋楼的居民,并仍然保持解决方案线性。
答案 0 :(得分:4)
我们可以使用增量来确定方向。我会解释我的意思。因为它涉及在其中一个建筑物中选择邮箱位置' (也就是说,不在两座建筑物之间):
选择其中一个建筑物作为枢轴(潜在邮箱位置)。根据建筑物相对于枢轴的位置对建筑物进行分区。在划分时,记录枢轴两侧最近的建筑物,以及(1)枢轴两侧居民的总数,以及(2)f(side, pivot)
代表每个建筑物的总和建筑'距离枢轴的距离乘以该建筑物内的居民数量。
现在我们有:
L pivot R
要确定是否可以为我们的选择做出改进,请尝试我们之前记录的每个最近的建筑物:
如果我们将选择一个建筑物向左移动,结果会如何变化?让我们调用左侧build_l
最近的建筑物,右侧build_r
。因此,我们选择向左移动一个建筑物的新结果将是:
左侧:
f(L, pivot)
- distance(build_l, pivot) * num_residents(build_l)
右侧:
f(R, pivot)
// we saved this earlier
+ total_residents(R) * distance(pivot, build_l)
+ num_residents(pivot) * distance(pivot, build_l)
执行类似的计算,将选择一个建筑物向右移动,以查看哪个产生较小的总数。然后选择产生改进的建筑物的一侧,并以类似的快速选择方式递归地划分它,直到找到最佳结果。另一方面,我们会跟踪到目前为止f
的居民总数和总结果,我们可以在新增内容的基础上更新。
答案 1 :(得分:0)
我将以以下方式解决(以下为伪代码)。
从左到右传递数组,并计算将所有居民j <= i放入邮箱i的费用。
# When we place mailbox at building i, all its residents contribute 0 to the total cost.
current_number_of_residents += residents_at_building[i-1]
# For each resident we've seen so far, the cost is increased by building_location[i] - building_location[i-1]
distance_delta = building_location[i] - building_location[i-1]
C_left[i] = C_left[i-1] + distance_delta * current_number_of_residents
然后,我们以相似的方式从右到左处理数组。 现在,我们可以通过检查最小和来找到最佳位置:
min_total_distance = min(min_total_distance, C_left[i] + C_right[i])
因为我们对数组进行了3次传递,所以时间复杂度为O(n)。要保留C_left和C_right数组,空间复杂度是O(n)。
快速选择算法的复杂度(由@גלעד-ברקן建议)平均也为O(n),但在最坏的情况下可以为O(n ^ 2)。因此,我没有看到这种方法比我建议的好处。欢迎任何评论。