Maximum Subsequence Sum Problem
Maximum subsequence sum problem:
Given (possibly negative) integers
A
1
,
A
2
,
⋯
,
A
N
A_1, A_2, \cdots ,A_N
A1,A2,⋯,AN, find the maximum value of
∑
k
=
i
j
A
k
\sum_{k = i}^j A_k
∑k=ijAk. (For convenience, the maximum subsequence sum is
0
0
0 if all the integers are negative.)
divide and conquer
The idea is to split the problem into two roughly equal subproblems, which are then solved recursively. This is the divide part.
The conquer stage consists of patching together the two solution of the subproblems, and possibly doing a small amount of additional work, to arrive at a solution for the whole problem.
In our case, the maximum subsequence sum can be in one of three places. Either it occurs entirely in the left half of the input, or entirely in the right half, or it crosses the middle and is in both halves.
The first two cases can be solved recursively.
The last case can be obtained by finding the largest sum in the first half that includes the last element in the first half, and the largest sum in the second half that includes the first element in the second half. The two sums can then be added together.
static int MaxSubSum(const int A[], int left, int right)
{
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBordedSum;
int LeftBordedSum, RightBorderSum;
int center, i;
if (left == right) //base case
if (A[left] > 0)
return A[left];
else
return 0;
//divide 求出 左边和右边的最大子序列和
center = (left + right) / 2;
MaxLeftSum = MaxSubSum(A, left, center);
MaxRightSum = MaxSubSum(A, center+1, Right);
//求包含中间数(左边序列的最右边数)的最大子序列
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for (i = center; i >= left; i--) {
LeftBorderSum += A[i];
if (LeftBorserSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
//求包含右边序列最左端数的最大子序列
MaxRightBorderSum = 0; RightBorderSum = 0;
for (i = center+1; i <= right; i++) {
RightBorderSum += A[i];
if (RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
求出 左边最大值, 右边最大值, 穿过中间(左边加右边)最大值,三个中最大值
return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubsequenceSum(const int A[], int N)
{
return MaxSubSum(A, 0, N-1);
}
Running time
Let T ( N ) T(N) T(N) be the time it takes to solve a maximum subsequence sum problem of size N N N.
If N = 1 N = 1 N=1, then the program takes constant amount of time to execute lines 7 to 10, which we shall call one unit. Thus T ( 1 ) = 1 T(1) = 1 T(1)=1.
Otherwise, the program must perform two recursive calls,the two for loops between lines 15 and 23, and some small aomont of bookkeeping, such as lines 11 and 24.
The two for loops combine to touch every element form A 0 A_0 A0 to A N − 1 A_{N-1} AN−1, and there is constant work inside the loops, so the time expended in lines 15 to 23 is O ( N ) O(N) O(N).
The code in lines 7 to 11, 14, 19, and 23 is all a constant amount of work and can thus be ignored compared with O ( N ) O(N) O(N).
The remainder of work is performed in line 12 and 13. These lines solve two subsequence problems of size N / 2 N/2 N/2(assuming N N N is even). Thus , these lines take T ( N / 2 ) T(N/2) T(N/2) units of time each, for a total of 2 T ( N / 2 ) 2T(N/2) 2T(N/2).
The total time is 2 T ( N / 2 ) + O ( N ) 2T(N/2) + O(N) 2T(N/2)+O(N).
This gives the equations:
T
(
1
)
=
1
T(1) = 1
T(1)=1
T
(
N
)
=
2
T
(
N
/
2
)
+
O
(
N
)
T(N) = 2T(N/2) + O(N)
T(N)=2T(N/2)+O(N)
To simplify the calculations, we can replace the O ( N ) O(N) O(N) term in the equation above with N N N; since T ( N ) T(N) T(N) will be expressed in B i g − O h Big-Oh Big−Oh notation anyway, this whill not affect the answer.
If T ( N ) = 2 T ( N / 2 ) + N T(N) = 2T(N/2) + N T(N)=2T(N/2)+N, and T ( 1 ) = 1 T(1) = 1 T(1)=1, then T ( 2 ) = 4 = 2 ∗ 2 T(2) = 4 = 2 * 2 T(2)=4=2∗2, T ( 4 ) = 12 = 4 ∗ 3 T(4) = 12 = 4 * 3 T(4)=12=4∗3, T ( 8 ) = 32 = 8 ∗ 4 T(8) = 32 = 8 * 4 T(8)=32=8∗4, T ( 16 ) = 80 = 16 ∗ 5 T(16) = 80 = 16 * 5 T(16)=80=16∗5.
If N = 2 k N = 2^k N=2k, then T ( N ) = N ∗ ( k + 1 ) = N l o g N + N = O ( N l o g N ) T(N) = N * (k+1) = NlogN + N = O(NlogN) T(N)=N∗(k+1)=NlogN+N=O(NlogN).
Kadane’s Algorithm
参见:Kadane’s Algorithm — (Dynamic Programming) — For new Solvers!
int MaxSubsequenceSum(const int A[], int N)
{
int ThisSum = 0, MaxSum = 0, j;
for (j = 0; j < N; j++) {
ThisSum += A[j];
if (ThisSum > MaxSum)
MaxSum = ThisSum;
else if (ThisSum < 0)
ThisSum = 0;
}
return MaxSum;
}
This algorithm is simpler to implement than the recursive algorithm and also is more efficient. It runs in O ( N ) O(N) O(N) time.