题目概述
给定 K K K个整数组成的序列{ N 1 , N 2 , … , N K N_1, N_2, …, N_K N1,N2,…,NK},“连续子列”被定义为{ N i , N i + 1 , … , N j N_i,N_{i+1}, …, N_j Ni,Ni+1,…,Nj},其中 1 ≤ i ≤ j ≤ K 1 ≤ i ≤ j ≤ K 1≤i≤j≤K。“最大子列和”被定义为所有连续子列元素的和中最大者。例如给定序列{ − 2 , 11 , − 4 , 13 , − 5 , − 2 -2, 11, -4, 13, -5, -2 −2,11,−4,13,−5,−2},其连续子列{ 11 11 11, − 4 -4 −4, 13 13 13}有最大的和 20 20 20。现要求你编写程序,计算给定整数序列的最大子列和。
输入格式
第一行输入正整数 K K K( 1 ≤ K ≤ 1 0 6 1≤K≤10^6 1≤K≤106);
第二行输入 K K K个整数,中间以空格分隔。
输出格式
在一行中输出最大子列和。
输入样例
6 6 6
− 2 -2 −2 11 11 11 − 4 -4 −4 13 13 13 − 5 -5 −5 − 2 -2 −2
输出样例
20 20 20
解决方法
- 分治法
取数列的中间点 a [ i 2 ] a{[\frac i2]} a[2i],那么最大子列的位置有三种情况:
(1)完全在左半边数组
(2)完全在右半边数组
(3)跨立在中间点两侧
若最大子列的位置为(1)或(2),可以利用递归解决。
若最大子列的位置为(3),则求中间点左半边数组的最大后缀与右半边数组的最大前缀。
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
int MaxSubArray(vector<int> a, int from, int to) {
if (from == to) {
return a[from];
}
int middle = (from + to) / 2;
int maxSum1 = MaxSubArray(a, from, middle);
int maxSum2 = MaxSubArray(a, middle + 1, to);
/*求左边数组的最大后缀*/
int currSum_left = a[middle], maxSum_left = a[middle];
for (int i = middle - 1; i >= from; i--) {
currSum_left += a[i];
if (currSum_left > maxSum_left) {
maxSum_left = currSum_left;
}
}
/*求右边数组的最大前缀*/
int currSum_right = a[middle + 1], maxSum_right = a[middle + 1];
for (int i = middle + 2; i <= to; i++) {
currSum_right += a[i];
if (currSum_right > maxSum_right) {
maxSum_right = currSum_right;
}
}
int maxSum3 = maxSum_left + maxSum_right;
return max(maxSum1, maxSum2, maxSum3);
}
- 分析法
记数组的前缀和 p [ i ] = a [ 0 ] + a [ 1 ] + . . . + a [ i ] p[i]=a[0]+a[1]+...+a[i] p[i]=a[0]+a[1]+...+a[i]
则子列和 s [ j + 1 , i ] = p [ i ] − p [ j ] s[j+1,i]=p[i]-p[j] s[j+1,i]=p[i]−p[j]
i : 0 → n − 1 i:0→n-1 i:0→n−1
计算数组的前缀和 p [ i ] p[i] p[i]
i : 0 → n − 1 i:0→n-1 i:0→n−1
求出 m i n = m i n ( p [ 0 ] , p [ 1 ] , . . . p [ i ] ) min=min(p[0],p[1],...p[i]) min=min(p[0],p[1],...p[i])
则 p [ i ] − m 即 为 以 a [ i ] p[i]-m即为以a[i] p[i]−m即为以a[i]结尾的数组的最大子列和
时间复杂度 O ( n ) O(n) O(n)
int MaxSubArray(vector<int> a) {
vector<int> p(a.size());
p[0] = a[0];
for (int i = 1; i < p.size(); i++) {
p[i] = p[i - 1] + a[i];
}
int min = 0, maxSum = p[0];
for (int i = 0; i < p.size(); i++) {
if (p[i] < min) {
min = p[i];
}
if (p[i] - min > maxSum) {
maxSum = p[i] - min;
}
}
return maxSum;
}
- 动态规划法
记 S [ i ] S[i] S[i]为以 a [ i ] a[i] a[i]结尾的数组的最大子列和
则 S [ i + 1 ] = m a x ( S [ i ] + a [ i + 1 ] , a [ i + 1 ] ) S[i+1]=max(S[i]+a[i+1], a[i+1]) S[i+1]=max(S[i]+a[i+1],a[i+1])
时间复杂度 O ( n ) O(n) O(n)
int MaxSubArray(vector<int> a) {
int maxSum = a[0];
int currSum = a[0];
for (int i = 1; i < a.size(); i++) {
if (currSum + a[i] > a[i]) {
currSum += a[i];
}
else {
currSum = a[i];
}
if (currSum > maxSum) {
maxSum = currSum;
}
}
return maxSum;
}