题目链接 https://leetcode.com/problems/apply-operations-to-maximize-frequency-score/description/

题目描述

在一个一维坐标轴上有 N 个村庄 (0-indexed),坐标 A[i] 为正整数。 给定最大交通成本 k.
我们准备设立一个邮局。选择一个区间 [L,R] 和邮局坐标 x, 定义交通成本 $c=\sum_{i=L}^{R}|x-A_i|$ 。 在满足 $c<=k$ 的前提下,找到最大的区间。

题目分析

我们先考虑一个子问题:对于给定的区间 [L,R],我们如何求出最低的交通成本。对此,我们先引入三个引理:

  1. $A_L <= x_{opt} <= A_R$ (Too Trivial)
  2. 最优的邮局坐标一定和某个村庄(M-th)重合,即 $x_{opt}=A_M$.
  3. 如果区间长度为奇数,则 $M_{opt}$ 就是最中心的一个村庄。如果为偶数,那最中间的两个村庄都是最优的选择.

在此基础上,我们可以通过如下变换,利用前缀和,在常数时间内求出特定区间的交通成本 $c$

\[\begin{aligned} c &= c_L + c_R \\ c_L &= \sum_{i=L}^{M} A_M - A_i \\ &= (M-L+1)A_M - \sum_{i=L}^{M}A_i \\ &= (M-L+1)A_M - ( \sum_{i=0}^{M}A_i - \sum_{i=0}^{L-1} A_i)\\ c_R &= \sum_{i=M}^{R} A_i - A_M \\ &= \sum_{i=0}^{R} A_i - \sum_{i=0}^{M-1}A_i - (R-M+1)A_M \end{aligned}\]

算法实现

有了如上的分析,实现 Sliding Window 算法就非常容易了。将所有坐标排序后,先求出前缀和 $P_V = \sum_{i=0}^{V} A_i$

我们将初始的 Window 设置为 $L=R=0$. 显然,此时 $c=0$,是一种合法的情况。按照通常的 Sliding Window 的做法,我们不断移动右边界 $R$,在 $c>k$ 时移动左边界 $L$. 我们的贪心算法会找出最优解。

引理证明

Lemma 2

使用反证法,假设 $A_M < x^* < A_{M+1} $, 此时交通成本
\(\begin{aligned} c &= c_L + c_R \\ &= \sum_{i=L}^{M}( x^* - A_i )+ \sum_{i=M+1}^{R} (A_i - x^*)\\ \end{aligned}\)

我们将邮局向左移动至 $A_M = x’ < x^* < A_{M+1} $, $x^*-x’=d>0$

左侧的 (M-L+1) 个村庄的成本会下降 (M-L+1)d, 而右侧的 (R-M) 个村庄的成本会上升 (R-M)d.

这样,我们分三种情况讨论:  

  1. 如果 $x^*$ 左侧的村庄更多,即 $M-L+1 > R-M$, 则 $x’=A_M$ 优于 $x^*$
  2. 如果两侧的村庄一样多,移动至 $x’=A_M$ 不会得到更差的结果  
  3. 如果右侧的村庄更多,易证 $x’'=A_{M+1}$ 优于 $x^*$

Lemma 2 得证。  

Lemma 3

可以沿用类似 Lemma 2 的计算方法。区间长为奇数时,根据引理,M = (R-L) / 2 + L。 如果我们要向左移动到 $A_{M-1}$, $A_M - A_{M-1}=d>0$。 左侧的村庄 [L, M-1] 的成本会下降,右侧 [M+1, R] 的成本会上升。这两部分抵消后,村庄 M 的交通成本会增加 d,得到一个更差的结果。

如果区间长为偶数,用同样的方法可以得到,在最中间的两个村庄之间移动邮局的 $\Delta=0$.

后记

这道题几个月前被丢到了我的 list上。我最开始没有意识到 Lemma 3, 而是试图维护一个三元数组 (L, M, R),在Sliding的过程中动态调整邮局的位置。顺着这个思路想下去,就是越想越偏离正解。 看了答案以后,意识到最优的邮局地点是固定的,也学到了借助前缀和来求特定区间的交通成本,从而避免了手动维护当前Window的成本。

在写题解的过程中,发现 Lemmy 2&3 的证明过程其实可以合并。我之前做这类题目时,对证明过程理解不到位。写这篇题解让我的认识深入了很多,也发现完全可以用很简明的方式证明。

LeetCode 原题里没有提到坐标,我借用 IOI2000 邮局 的题目背景,感觉直观了不少。