以这样的方式填充数组,使得每个元素等于两个数的最小总和

时间:2014-06-18 19:03:31

标签: c++ arrays algorithm data-structures

给定一个数组(仅包含个整数)已经有第一个 k 元素: a 1 ,一个 2 ,.... a k
我需要填充剩余的(n - k)元素(数组总共有 n 个元素)。
n 的值约为 10 ^ 3 1 <= k <= n
每个 a i 的值是两个数字的最小和,使得这两个数字的位置之和等于 i

这是伪代码(我的算法):

for i = k + 1 to n
  a[i] = max_value
  for j = 1 to (i / 2)
    a[i] = min(a[i], a[j] + a[i - j])

时间复杂度: O(n ^ 2)

问题:还有其他方法可以更快地完成这项工作吗?
我正在寻找能够在小于 O(n)中找到每个 a i 的值的任何数据结构或算法。

P / S:这是我程序中的一个程序,所以我需要尽快完成。

2 个答案:

答案 0 :(得分:0)

您可以通过使用线程并行检查最小值来提高程序速度。例如,您可以运行4个线程,每个线程检查j的范围的1/4。这将略微提高速度,但您的算法仍将需要O(n ^ 2)运行时间。

我同意你最有可能超越O(n ^ 2)的评论。所以你最好的选择可能是尝试这样的事情来优化你的代码,以减少n ^ 2前面的系数。

答案 1 :(得分:0)

想法1

AFAICT这不会对O(n ^ 2)提供有保证的改进,但它应该在实践中将内循环周期的数量减少很多。基本思想是我们可以以不同的顺序在内循环中测试对,这使我们能够在很多时候提前完成。具体来说,我们首先制作一个排序的数字位置列表,并将其存储在s[]中,以便a[s[i]]a[]中的第i个最小数字。然后在主内循环中,我们使用a[s[j]]而不是a[j](和a[i - s[j]]而不是a[i - j])以第一项的递增顺序形成对和。这为我们提供了两种方法来提前停止内循环:

  1. 如果a[s[j]] >= a[i],那么我们可以停止,因为每个总和必须更大,因为每个总和的第一项(a[s[j+1]]等)必须至少与最佳解决方案一样大远(已经在a[i]),而另一个术语永远不会消极。
  2. 如果a[i - s[j]] <= a[s[j]](即,如果&#34;伙伴&#34;下一个最小数字小于或等于它),我们可以停止,原因更复杂。相反,假设后来有一些更好的配对a[s[m]] + a[i - s[m]](即m > j)。我们知道第一个词a[s[m]],必须至少与我们当前的第一个词a[s[j]]一样大,因为我们按递增顺序访问第一个词,m > j;因此,对于总和a[s[m]] + a[i - s[m]]要比a[s[j]] + a[i - s[j]]更好(即更少),它的第二个术语a[i - s[m]]必须小于我们当前的第二个术语{{{ 1}}。 (这不是一个充分条件,但这并不重要。)但是,由于我们刚刚观察到a[i - s[j]],我们也知道a[i - s[j]] <= a[s[j]],这意味着a[i - s[m]] < a[s[j]]必须在我们之前已经处理过的一对总和中出现作为第一个术语!这与a[i - s[m]]相矛盾,这意味着没有更好的配对可以存在,所以我们可以安全地停止。
  3. 我希望第二个条件可以消除很多内循环周期;第一个可能只对数据集有很大帮助,这些数据集中有一些小数字和很多非常高的数字,并且可以用小数字的总和来覆盖大多数位置。

    奖金效率:如果我们实施上述第二个条件,那么我们实际上并不需要单独的m > j循环终止测试,因为在检查了任何j < i / 2对和之后,我们必须遇到至少有一对和两次(一次交换第一和第二项),这将导致条件2触发并退出循环。

    伪代码:

    i / 2 + 1