给定阵列A,形成阵列M,使得乘积之和(a1 * m1 + ... + an * mn)最大

时间:2013-11-23 06:07:11

标签: algorithm

我最近接受了一次采访,在那里我被问到以下算法问题。我无法找到O(n)解决方案,也无法找到谷歌的问题。

  

给定一个整数的数组A [a_0 ... a_(n-1)](+ ve和-ve)。形成一个   数组M [m_0 ... m_(n-1)]其中m_0 = 2,m_i在[2,...,m_(i-1)+1]中   这样产品总和最大,即我们必须最大化a_0 * m_0   + a_1 * m_1 + ... + a_(n-1)* m_(n-1)

示例

input  {1,2,3,-50,4}
output {2,3,4,2,3}

input  {1,-1,8,12}
output {2,3,4,5}

我的O(n ^ 2)解决方案是从m_0 = 2开始并且只要a_i是+ ve就继续递增1。如果a_i< 0我们必须考虑从2到m_i-1 + 1的所有m_i,并查看哪一个产生最大乘积之和。

请建议线性时间算法。

2 个答案:

答案 0 :(得分:2)

假设您有以下数组:

1, 1, 2, -50, -3, -4, 6, 7, 8.

在每个条目中,我们可以继续递增进度或将值重置为较低的值 这里只有两个不错的选择。我们要么选择当前条目的最大可能值,要么选择最小值(2)。 (最后的证明)

现在很清楚,输出中的前3个条目应为2,3和4(因为到目前为止所有数字都是正数,没有理由将它们重置为2(低值)。

遇到否定录入时,计算总和:
-(50 + 3 + 4) = -57

接下来计算后续+ ve连续数字的相似总和 (6 + 7 + 8) = 21

由于57大于21,因此将第4个条目重置为2是有意义的。

再次计算否定条目的总和:
-(3 + 4) = -7

现在7小于21,因此不再重置是有道理的,因为如果正值很高,则应获得最大乘积。

输出数组应为:

2, 3, 4, 2, 3, 4, 5, 6, 7

要使此算法在线性时间内工作,您可以预先计算计算中所需的总和数组。

<强>证明:

当遇到负数时,我们可以将输出值重置为低值(比如j)或继续我们的增量(比如说i)。

假设有k -ve值且m为正值。

如果我们将值重置为j,那么这些k -ve值和m + ve值的乘积值应等于:

- ( (j-2+2)*a1 + (j-2+3)*a2 + ... + (j-2+k+1)*ak ) + ( (j-2+k+2)*b1 + (j-2+k+3)*b2 + ... + (j-2+k+m+1)*am )

如果我们不将值重置为2,那么这些k -ve值和m + ve值的乘积值应等于:

- ( (i+2)*a1 + (i+3)*a2 + (i+4)*a3 ... + (i+k+1)*ak ) + ( (i+k+2)*b1 + (i+k+3)*b2 + ... + (i+k+m+1)*am )

因此,上述两个表达式之间的区别是:

(i-j+2)* ( sum of positive values - sum of negative values )

此数字可以是正数也可以是负数。因此,我们倾向于使j尽可能高(M [i-1] +1)或尽可能低(2)。

在O(N)时间内预先计算总和数

编辑:正如Evgeny Kluev指出

  1. 向后遍历阵列。
  2. 如果遇到负面元素,请忽略它。
  3. 如果遇到正数,请将后缀和等于该值。
  4. 继续将元素的值添加到总和中,直到它保持为正。
  5. 一旦总和变为&lt; 0,注意这一点。这就是我们将重置决定分为2并继续增量的观点。
  6. 再次忽略所有负值,直到达到正值。
  7. 继续重复直到到达阵列的末尾。
  8. 注意:在计算后缀总和时,如果我们遇到零值,那么可以有多个这样的解决方案。

答案 1 :(得分:1)

感谢Abhishek Bansal和Evgeny Kluevfor的伪代码。 这是Java中的代码。

 public static void problem(int[] a, int[] m) {
        int[] sum = new int[a.length];
        if(a[a.length-1] > 0)
          sum[a.length-1] = a[a.length-1];
        for(int i=a.length-2; i >=0; i--) {
          if(sum[i+1] == 0 && a[i] <= 0) continue;
          if(sum[i+1] + a[i] > 0) sum[i] = sum[i+1] + a[i];
        }
        //System.out.println(Arrays.toString(sum));
        m[0] = 2;
        for(int i=1; i < a.length; i++) {
          if(sum[i] > 0) {
            m[i] = m[i-1]+1;
          } else {
            m[i] = 2;
          }
        }
      }