问题描述
给定一个序列 a n {a_n} an,对于其所有连续子序列 a i . . . a j ( 0 < = i < = j < n ) a_i...a_j(0<=i<=j<n) ai...aj(0<=i<=j<n),求出这样的子序列的和的最大值。如序列 { 4 , − 3 , 5 , − 2 , 6 , − 7 } \{4,-3,5,-2,6,-7\} {4,−3,5,−2,6,−7}的子序列 { 4 , − 3 , 5 , − 2 , 6 } \{4,-3,5,-2,6\} {4,−3,5,−2,6}的和10是所有连续子序列的和中最大的。
线性算法
复杂度
O
(
n
)
O(n)
O(n)
若序列
a
n
a_n
an中至少有一个正值,则具有最大和的连续子序列不存在小于等于0的前缀,因为若存在,则去除该前缀则和不会变小,导致矛盾。设一个起点,从起点处往后加得的和一旦小于等于0,则将起点移到加的最后一个元素的下一位置。
void get_max_subsequence_sum_linear()
{
int current_sequence_sum = a[0];
for (int i = 1; i < n; i++)
{
if (current_sequence_sum <= 0)
{
current_sequence_sum = a[i]; // 从下一个元素开始累加
}
else
{
current_sequence_sum += a[i];
}
if (max_sum < current_sequence_sum)
{
max_sum = current_sequence_sum;
}
}
}
动规
复杂度
O
(
n
)
O(n)
O(n)
设
p
[
i
−
1
]
p[i-1]
p[i−1]为序列
a
0
.
.
.
a
i
−
1
a_0...a_{i-1}
a0...ai−1包含
a
i
−
1
a_{i-1}
ai−1在内的子序列和的最大值,则
1、若
p
[
i
−
1
]
p[i-1]
p[i−1]>0,则
p
[
i
]
=
p
[
i
−
1
]
+
a
i
p[i]=p[i-1]+a_i
p[i]=p[i−1]+ai
2、否则,
p
[
i
]
=
a
i
p[i]=a_i
p[i]=ai
即
p
[
i
]
=
m
a
x
(
p
[
i
−
1
]
+
a
i
,
a
i
)
p[i]=max(p[i-1]+a_i, a_i)
p[i]=max(p[i−1]+ai,ai)
由递推式求得所有
p
[
i
]
p[i]
p[i]后,最大的
p
[
i
]
p[i]
p[i]即为最大连续子序列和。
void get_max_subsequence_sum_dp()
{
int p[n]; // p[i]表示序列a[0]...a[i]包括a[i]在内的最大子序列和
p[0] = a[0];
for (int i = 1; i < n; i++)
{
if (p[i-1] > 0)
{
p[i] = p[i-1] + a[i];
}
else
{
p[i] = a[i];
}
}
for (int i = 0; i < n; i++)
{
if (max_sum < p[i])
{
max_sum = p[i];
}
}
}
分治
复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
将序列等分为左右两块,考虑具有最大和的连续子序列包不包含位于中间的元素:
1、若不包含,则该连续子序列或者在左子序列,或者在右子序列。
2、若包含,则只要从中间分别向左和向右累加,求得最大和left_max_sum和right_max_sum,然后将左最大和与右最大和加起来。
因此最大连续子序列和是这三种情况的结果的最大值。
伪代码如下:
int get_max_subsequence_sum(low, high) // 包括low,不包括high
{
if (low == high - 1)
return a[low];
sum = 0
for i from mid to low:
sum += a[i];
if (sum > max_left_sum)
max_left_sum = sum;
sum = 0;
for i from mid+1 to high-1:
sum += a[i];
if (sum > max_right_sum)
max_right_sum = sum;
return max(max_left_sum+max_right_sum,
max(get_max_subsequence_sum(low,mid),
get_max_subsequence_sum(mid+1,high)));
}
C++代码见github
暴力1
复杂度 O ( n 2 ) O(n^2) O(n2)
void get_max_subsequence_sum_brute1()
{
for (int i = 0; i < n; i++)
{
int sum = 0;
for (int j = i; j < n; j++)
{
sum += a[j];
if (sum > max_sum)
{
max_sum = sum;
}
}
}
}
暴力2
遍历所有子序列,复杂度 O ( n 3 ) O(n^3) O(n3)
void get_max_subsequence_sum_brute2()
{
for (int i = 0; i < n; i++)
{
for (int j = i; j < n; j++)
{
int sum = 0;
for (int k = i; k <= j; k++)
{
sum += a[k];
}
if (sum > max_sum)
{
max_sum = sum;
}
}
}
}