因此对于以下数组,其中L = 3
-5 -1 2 -3 0 -3 3
至少长度为3的最佳总和为0,其中子序列是最后三个元素(0,-3,3)
如何以比O(NL)更快的速度计算任何数组的总和(如果L == 0),有效地为O(N ^ 2)?
答案 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],我们必须选择两种情况之间的最大值:
第一种情况的结果可以使用部分和数组(S [i] = A [1] + A [2] + ... + A [i]),S [i] -S立即计算[iL的]。第二种情况的结果是REZ [i-1] + A [i]。 所以,
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;
}