我相信我能做到这一点是 O(n log n)。
首先,对B 数组进行排序,对A 数组应用相同的排列(并记住排列)。这是 O(n log n) 部分。由于我们对所有 i 求和,因此对 A 和 B 数组应用相同的排列不会改变最小值。
使用排序的B 数组,算法的其余部分实际上是 O(n)。
对于每个k,定义一个数组Ck[i] = |B[i] - B[k]|
(注意:我们不会实际构造Ck...我们只是将其用作一个概念,以便于推理。)
观察我们试图最小化的数量(超过 k)是 A[i] * Ck[i] 的总和。让我们继续给它起个名字:
定义:Sk = Σ A[i] * Ck[i]
现在,对于任何特定的 k,Ck 是什么样的?
嗯,很明显,Ck[k] = 0。
更有趣的是,由于B数组是排序的,我们可以去掉绝对值符号:
- Ck[i] = B[k] - B[i],对于 0
- Ck[i] = 0,对于 i = k
- Ck[i] = B[i] - B[k],对于 k
让我们再定义两件事。
定义:Tk = Σ A[i] for 0
定义:Uk = Σ A[i] for k
(即Tk是A的前k-1个元素之和。Uk是A中除前k个元素之外的所有元素之和.)
关键观察:给定 Sk、Tk 和 Uk,我们可以计算 Sk+1、Tk+1 和 Uk+1 在恒定时间内。怎么样?
T 和 U 很简单。
问题是,我们如何从 Sk 到 Sk+1?
考虑当我们转到 Ck+1 时 Ck 会发生什么。我们只需将 B[k+1]-B[k] 加到 C 中从 0 到 k 的每个元素上,然后从 C 中从 k+1 到 n 的每个元素中减去相同的数量(证明这一点)。这意味着我们只需要添加 Tk * (B[k+1] - B[k]) 并减去 Uk * (B[k+1] - B[k]) 从 Sk 到 Sk+1。
代数... Sk 的前 k 项只是 A[i] * (B[k] - B[i]) 从 0 到 k-1 的总和。
S的前k项k+1是A[i] * (B[k+1] - B[i])从0到k-1的和
它们之间的差异是 (A[i] * (B[k+1] - B[i]) - (A[i] * (B[k]) 从 0 到 k-1 的总和- B[i])). 分解出 A[i] 项并取消 B[i] 项以获得 A[i] * (B[k+1] - B[ 的 0 到 k-1 的总和k]),也就是 Tk * (B[k+1] - B[k])。
对于 Sk 的最后 n-k-1 项类似。
由于我们可以在线性时间内计算 S0、T0 和 U0,我们可以从 S k 到 Sk+1 在常数时间内,我们可以在线性时间内计算所有的 Sk。这样做,记住最小的,你就完成了。
使用逆排序排列得到原始数组的k。