【问题标题】:Given two arrays find the index k that minimizes the sum A[i]*|B[i]-B[k]|给定两个数组,找到最小化和 A[i]*|B[i]-B[k]| 的索引 k
【发布时间】:2013-01-07 18:48:21
【问题描述】:

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

有什么想法吗?谢谢!

【问题讨论】:

  • @Dukeline:我认为他的意思是绝对价值。

标签: arrays algorithm data-structures


【解决方案1】:

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

一旦对数组进行排序,如果 i>k 则 B[i]>=B[k] 并且如果 i

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 值。

【讨论】:

    【解决方案2】:

    我相信我能做到这一点是 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

    【讨论】:

    • +1 表示正确的算法 - 我的答案省略了应用逆排序排列
    【解决方案3】:

    这是 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).

    【讨论】:

      猜你喜欢
      • 2014-12-29
      • 2021-11-01
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-02
      • 2020-06-21
      • 2011-06-26
      相关资源
      最近更新 更多