最小连续子序列和最小长度L

时间:2011-10-22 17:36:28

标签: algorithm dynamic-programming arrays

因此对于以下数组,其中L = 3

-5 -1 2 -3 0 -3 3

至少长度为3的最佳总和为0,其中子序列是最后三个元素(0,-3,3)

如何以比O(NL)更快的速度计算任何数组的总和(如果L == 0),有效地为O(N ^ 2)?

5 个答案:

答案 0 :(得分:21)

答案 1 :(得分:3)

可以在O(n)中使用动态编程。

1。)对于数组中的每个索引i,将部分总和存储到i

2。)将最小总和的索引存储到i

3.)对于数组中的每个索引i,将最大值存储为i,这是i的部分总和减去步骤2中确定的索引的部分和,即Min(Sum(k))k < =我记住子序列必须至少长度为L的约束。

所有这一切都可以在一个循环中在O(n)中完成。

现在,对于数组中的每个索引i,您的最大总和达到i,您可以确定连续子序列和该子序列的结束索引的最大总和。当您有结束索引时,您可以向后走,直到达到最大值。这两个操作都是O(n)。

C#中的示例实现:

int [] values = {-5, -1, 2, -3, 0, -3, 3};
int L = 3;

int[] sumUpTo = new int [values.Length];
int[] minUpTo = new int[values.Length];
int[] maxUpTo = new int[values.Length];

for (int i = 0; i < values.Length; i++)
{
    sumUpTo[i] = values[i];
    minUpTo[i] = i;
    if (i > 0)
    {
        sumUpTo[i] += sumUpTo[i - 1];
        minUpTo[i] = sumUpTo[i] < sumUpTo[i - 1] ? i : minUpTo[i - 1];
    }
    maxUpTo[i] = sumUpTo[i] - ((i >= L && sumUpTo[minUpTo[i - L]] < 0) ? sumUpTo[minUpTo[i - L]] : 0);
}


int maxSum = int.MinValue;
int endIndex = -1;

for (int i = L-1 ; i < values.Length; i++)
    if(maxUpTo[i] > maxSum)
    {
        endIndex = i;
        maxSum = maxUpTo[i];
    }

//Now walk backwards from maxIndex until we have reached maxSum
int startIndex = endIndex;
int currentSum = values[startIndex];

while (currentSum != maxSum || (endIndex - startIndex < L-1))
{
    startIndex--;
    currentSum += values[startIndex];

}

Console.WriteLine("value of maximum sub sequence = {0}, element indexes {1} to {2}", maxSum, startIndex, endIndex);

答案 2 :(得分:0)

Here is the JAVA version : 

Note : Credit goes to @templatetypedef. That explanation is awesome.


 public static int max_sum_in_subarray_of_minimum_length(int [] array, int min_length){

    int running_sum=0, max_sum_up_to_here=0, max_sum=0;

    int begin=0, end=0, max_start=0;

  /* max_sum_up_here = sum of all elements in array up to length L */

    for(int i=0;i<min_length;i++){

        max_sum_up_to_here+=array[i];
    }

  /* running sum and max sum = max_sum_up_here */

    running_sum = max_sum_up_to_here;
    max_sum= running_sum;

  /* Iterate through all elements starting from L i.e minimum length */

    for(int i=min_length;i<array.length;i++){

  /* min_sum_up_to_here = min_sum_up_to_here +
   next element in array - (i-L)th element in array */    

        max_sum_up_to_here+=array[i]-array[i-min_length];

  /* if running_sum + next element in array > max_sum_up_to here then 
         running_sum = running_sum + next element in array 
     else running_sum = max_sum_up_to_here */

        if( (running_sum+array[i]) > max_sum_up_to_here ){


            running_sum = running_sum+array[i];
            max_start = i-min_length+1;

         }else{

            running_sum= max_sum_up_to_here;
        }

     /* if running sum > max_sum then max_sum = running sum */

        if( max_sum < running_sum ){


            max_sum = running_sum;

            begin =max_start;

            end=i;

        }

    }

     /* max_sum gives sum of contiguous sub array of length L and begin and end gives indexes of the sub array*/  

    return max_sum;
}

答案 3 :(得分:0)

无用的案例和定义等我的解决方案是自然的。 首先,记住这一点,我们正在寻找整数数组的连续片段的最大总和,该片段具有多个或恰好是L个元素。我们将A命名为初始数组。出于与Kadane算法相同的原因,我们考虑一个辅助数组,REZ,具有N个元素,如A,REZ [i]表示A的连续片段的最大和,包含至少L个元素并且恰好在i处结束 - 位置。当然,REZ [1],RZ [2],REZ [L-1]都等于ZERO或-INFINITY值。 REZ [L] = A [1] + A [2] + ... + A [L]。 对于REZ中的其余值,从i从L + 1增长到N,要计算REZ [i],我们必须选择两种情况之间的最大值:

  1. 恰好为L值且包含A [i]
  2. 的片段
  3. 具有多于L个值且含有A [i]
  4. 的片段

    第一种情况的结果可以使用部分和数组(S [i] = A [1] + A [2] + ... + A [i]),S [i] -S立即计算[iL的]。第二种情况的结果是REZ [i-1] + A [i]。 所以,

    • REZ [i] = - INFINITY,如果1&lt; = i&lt; = L-1
    • REZ [i] = S [i],如果i = L
    • REZ [i] = max(S [i] -S [i-L],REZ [i-1] + A [i]),如果i> L。

    REZ建成后,我们必须计算其最大值。 让我们考虑以下示例:

    N = 7

    A -5 -1 2 -3 0 -3 3

    L = 3

    S -5 -6 -4 -7 -7 -10 -7

    REZ:-INF -INF -4

    REZ [4] = max(S [4] -S [4-3],REZ [3] + A [4])= max(-2,-7)= - 2

    REZ:-INF -INF -4 -2

    REZ [5] = MAX(S [5] -S [5-3],REZ [4] + A [5])= MAX(-1,-2)= - 1

    REZ:-INF -INF -4 -2 -1

    REZ [6] = max(S [6] -S [6-3],REZ [5] + A [6])= max(-6,-4)= - 4

    REZ:-INF -INF -4 -2 -1 -4

    REZ [7] = max(S [7] -S [7-3],REZ [6] + A [7])= max(0,-1)= 0

    REZ:-INF -INF -4 -2 -1 -4 0

    REZ中的最大值为0,这是整个问题的答案。

    我希望我的英语足够好。我正在寻找类似问题的解决方案,当结果必须包含最多L个连续元素时。当我意识到上述方法实际上是针对具有至少L个元素的解决方案时,我非常失望。

答案 4 :(得分:0)

以下是我的Java实现。

public static int maxSumSubsequenceGivenLength(int[] array, int l) {

    if (null == array || array.length < l) {
        return -1;
    }

    int previousSequenceSum = 0;

    for (int i = 0; i < l; i++) {
        previousSequenceSum += array[i];
    }

    int maxSum = previousSequenceSum;
    int currentSum = 0;
    int startIndexFinal = 0;
    int endIndexFinal = l - 1;

    for (int i = l; i < array.length; i++) {
        currentSum = previousSequenceSum + array[i] - array[i - l];
        if (currentSum > maxSum) {
            maxSum = currentSum;
            endIndexFinal = i;
            startIndexFinal = i - l + 1;
        }
        previousSequenceSum = currentSum;
    }

    System.out.println("start index:" + startIndexFinal + " end index: " + endIndexFinal);
    return maxSum;
}