问题描述:给定一个整数序列(可能有负数),求一子序列(记为L‘)使得该子序列所有元素之和最大。例:给定序列-2,11,-4,13,-5,-2,则最大子序列和为20(11,-4,13)
方法一:遍历穷举——O(n^2)
略
方法二:分治递归——O(n*logn)
思路:将输入序列L分为左右两个子序列L1和L2,则L’只可能以下面三种情况出现:
- L‘完全在L1中,则L’也是L1中具有最大子序列和的子序列
- L‘完全在L2中,则L’也是L2中具有最大子序列和的子序列
- L‘跨越L1和L2,则分别求出L1中包含最后一个元素的所有序列中的和最大的那个(记为L1’)以及L2中包含第一个元素的所有序列中的和最大的那个(记为L2‘),则L’=L1‘+L2’
因此,前两种情况实际上是一种递归,对于情况三,可以采用遍历。最后,求出三种情况中最大的那个,即是所求最终结果
方法三:动态规划——O(n)
思路:不断计算子序列的和,若大于当前最大子序列和,则更新;若当前子序列和小于0,只会为后续序列增加负担,因此全部丢弃,继续扫描(思考一下,L‘中包含最左边元素的任意子序列的和都不可能小于0,否则L’就不可能是L中具有最大和的子序列)
想想,若子序列(A[s], ..., A[t])之和小于0,为什么全部丢弃而不考虑(A[s+1], ..., A[t])或者(A[s+2], ... A[t])呢?因为既然走到这一步,说明(A[s], ..., A[t])左边任意部分如(A[s])或者(A[s], A[s+1])它们各自的和不可能小于0,否则早就被丢弃了。因此,(A[s+1], ..., A[t])或者(A[s+2], ... A[t])只可能比(A[s], ..., A[t])更小。也就是说,若(A[s], ..., A[t])小于0,则(A[s+1], ..., A[t])或者(A[s+2], ... A[t])必然小于0,带上它们只会给后续序列增加负担
利用动态规划算法的代码如下:
int max_subseq_sum(int a[], int n){
int this_sum = 0, max_sum = 0;
for(int i = 0; i < n; i++){
this_sum += a[i];
if(this_sum > max_sum)
max_sum = this_sum;
else if(this_sum < 0) //丢弃很关键
this_sum = 0;
}
return max_sum;
}