给定两个数组找到最小化和A [i] * | B [i] -B [k] |的索引k

时间:2013-01-07 18:48:22

标签: arrays algorithm data-structures

我得到两个包含自然数A和B的数组,我需要找到最小化和A [i] * | B [i] -B [k] |的索引k。从i = 0到n-1。 (两个数组的长度相同) 它在O(n ^ 2)中显然很容易,我只计算0和n-1之间所有k的所有和,但我需要更好的运行时复杂度。

有什么想法吗?谢谢!

3 个答案:

答案 0 :(得分:8)

您可以在时间O(nlogn)中执行此操作,首先根据B中的值对两个数组进行排序,然后执行单次扫描。

一旦数组被排序,则如果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的值,以获得最佳分数。

答案 1 :(得分:5)

我相信我能做到的就是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数组已经排序,我们可以摆脱绝对值符号:

  • C k [i] = B [k] -B [i],0 <= i <1。 ķ
  • C k [i] = 0,i = k
  • C k [i] = B [i] -B [k],对于k <我&lt; Ñ

让我们再定义两件事。

定义:T k =ΣA[i] 0 <= i

定义:U k =ΣA[i]对于k <我&lt; Ñ

(即,T k 是A的第一个k-1个元素的总和.U k 是除A的前k个元素之外的所有元素的总和。)

关键观察:给定S k ,T k 和U k ,我们可以计算S k + 1 < / sub>,T k + 1 ,并且U k + 1 在恒定时间内。怎么样?

T和U很容易。

问题是,我们如何从S k 到S k + 1

当我们转到C k + 1 时,考虑C k 会发生什么。我们简单地将B [k + 1] -B [k]添加到从0到k的C的每个元素,并且我们从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之和。 / p>

S k + 1 的前k项是A [i] *(B [k + 1] - B [i])

这些之间的差异是(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]的0到k-1之和k]),它只是T k *(B [k + 1] - B [k])。

类似于S k 的最后一个n-k-1项。

因为我们可以在线性时间内计算S 0 ,T 0 和U 0 ,我们可以从S 开始k 到S k + 1 在恒定时间内,我们可以计算线性时间内的所有S k 。所以,记住最小的,你就完成了。

使用排序排列的倒数来获取原始数组的k

答案 2 :(得分:3)

这是O(NlogN)解决方案。 示例

A 6 2 5 10 3 8 7
B 1 5 4 3  6 9 7

1)首先将两个数组排序为增加B的顺序.A&#39; s元素仅与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).