我有两个包含自然数 A 和 B 的数组,我需要找到使总和 A[i] * |B[i]-B[k]| 最小化的索引 k 从 i=0 到 n-1。(两个数组的长度相同)在 O(n^2) 中显然很容易做到,我只计算 0 到 n-1 之间的所有 k 的所有总和,但我需要更好的运行时间复杂度。
有任何想法吗?谢谢!
我有两个包含自然数 A 和 B 的数组,我需要找到使总和 A[i] * |B[i]-B[k]| 最小化的索引 k 从 i=0 到 n-1。(两个数组的长度相同)在 O(n^2) 中显然很容易做到,我只计算 0 到 n-1 之间的所有 k 的所有总和,但我需要更好的运行时间复杂度。
有任何想法吗?谢谢!
您可以通过首先根据 B 中的值对两个数组进行排序,然后执行单次扫描,在 O(nlogn) 时间内完成此操作。
数组排序后,如果 i>k 则 B[i]>=B[k] 并且如果 i<= k 则 B[i]<=B[k],因此总和可以重写为:
sum A[i] * abs(B[i]-B[k]) = sum A[i]*(B[i]-B[k]) for i=k..n-1
+ sum A[i]*(B[k]-B[i]) for i=0..k-1
= sum A[i]*B[i] for i=k..n-1
- B[k] * sum A[i] for i=k..n-1
+ B[k] * sum A[i] for i = 0..k-1
- sum A[i]*B[i] for i = 0..k-1
您可以预先计算时间 O(n) 中的所有总和,然后您可以评估 O(n) 中每个位置的目标总和,并选择给出最佳分数的 k 值。
我相信我能做到这一点是 O(n log n)。
首先,对数组进行排序,对B
数组应用相同的排列A
(并记住排列)。这是 O(n log n) 部分。由于我们对所有 i 求和,因此对 A 和 B 数组应用相同的排列不会改变最小值。
使用排序B
数组,算法的其余部分实际上是 O(n)。
对于每个 k,定义一个数组 C k [i] = |B[i] - B[k]|
(注意:我们实际上不会构造 C k ...我们只是将它用作一个概念,以便于推理。)
观察我们试图最小化(超过 k)的数量是 A[i] * C k [i] 的总和。让我们继续给它一个名字:
定义: S k = Σ A[i] * C k [i]
现在,对于任何特定的 k,C k是什么样的?
嗯,很明显,C k [k] = 0。
更有趣的是,由于 B 数组是排序的,我们可以去掉绝对值符号:
让我们再定义两件事。
定义: T k = Σ A[i] for 0 <= i < k
定义: U k = Σ A[i] for k < i < n
(也就是说,T k是 A 的前 k-1 个元素的总和。U k是除 A 的前 k 个元素之外的所有元素的总和。)
关键观察:给定 S k、 T k和 U k,我们可以在恒定时间内计算 S k+1、 T k+1和 U k+1 。如何?
T 和 U 很简单。
问题是,我们如何从 S k到 S k+1?
考虑当我们转到 C k +1时 C k 会发生什么。我们只需将 B[k+1]-B[k] 加到 C 中从 0 到 k 的每个元素上,然后从 C 中从 k+1 到 n 的每个元素中减去相同的数量(证明这一点)。这意味着我们只需添加 T k * (B[k+1] - B[k]) 并减去 U k * (B[k+1] - B[k]) 即可从 S k到 S k+ 1 .
代数... S k的前 k 项只是 A[i] * (B[k] - B[i]) 从 0 到 k-1 的总和。
S k+1的前 k 项是 A[i] * (B[k+1] - B[i]) 从 0 到 k-1 的总和
它们之间的差异是 (A[i] * (B[k+1] - B[i]) - (A[i] * (B[k] - B[ i])). 分解出 A[i] 项并取消 B[i] 项以获得 A[i] * (B[k+1] - B[k]) 从 0 到 k-1 的总和,也就是 T k * (B[k+1] - B[k])。
同样对于 S k的最后 nk-1 项。
因为我们可以在线性时间内计算 S 0、T 0和 U 0,并且我们可以在恒定时间内从 S k到 S k+1 ,所以我们可以在线性时间内计算所有的 S k。所以这样做,记住最小的,你就完成了。
使用排序排列的逆来获取k
原始数组的 。
这是 O(NlogN) 解决方案。例子
A 6 2 5 10 3 8 7
B 1 5 4 3 6 9 7
1)首先将两个数组排序为B的顺序增加。A的元素只是与B绑定。排序后,我们得到
A 6 10 5 2 3 7
B 1 3 4 5 6 7
由于B现在井井有条。我们有
n-1
sum A[i]|B[i]-B[k]|
i=0
k-1 n-1
=sum A[i](B[k]-B[i])+ sum A[i](B[k]-B[i])
i=0 i=k+1
k-1 n-1 k-1 n-1
=B[k](sum A[i] -sum A[i]) - (sum A[i]B[i]- sum A[i]B[i])
i=0 i=k+1 i=0 i=k+1
2) 我们计算数组 A 的前缀和 sumA=0 6 16 21 23 26 33
i=e
With sumA sum A[i] can be calcuated in O(1) time for any s and e.
i=s
出于同样的原因,我们可以计算 A[i]B[i] 的前缀和。所以对于每个 k,要检查它的值,只需要 O(1) 时间。所以总时间复杂度是O(NlogN)+O(N).