题目
给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大
方法一 暴力枚举每一子串
直接枚举起点终点取最大子段
时间复杂度:O(n^3)
int MaxSubSequenceSum(int a[],int Left,int Right){
int thissum;
int res = 0;
int i;
int j;
int k;
for(i = Left; i <=Right; ++i){
for(j = i;j <= Right; ++j){
thissum = 0;
for(k = i;k <= j;++k)
thissum += a[k];
res = res > thissum ? res : thissum;
}
}
return res;
}
方法二 优化暴力枚举
方法一第三层循环可以直接累加进thissum
不用每个都来一遍
时间复杂度:O(n2)
int MaxSubSequenceSum(int a[],int Left,int Right){
int thissum;
int res = 0;
int i;
int j;
for(i = Left; i <=Right; ++i){
thissum = 0;
for(j = i;j <= Right; ++j){
thissum += a[j];
res = res > thissum ? res : thissum;
}
}
return res;
}
方法三 分治
将串分为两部分 [l, mid] 与 [mid +1, r]
最大和子段 [i, j]必处于三种情况之一
-
l=<i <=j<=mid
-
l=<i <=mid<=j
-
mid + 1<=i <= j
即
左半边最大字段和
或者
右半边最大子段和
或者
跨越左右的最大子段和
int MaxSubSequenceSum(int a[],int Left,int Right){
int ans = 0;
if(Left == Right)
return a[Left] ;
int mid = (Left + Right)>>1;
int ans1 = 0;
int ans2 = 0;
int thissum = 0;
int i = 0;
//实现跨越左右的关键
for(i = mid;i >= Left;--i){
thissum += a[i];
if(thissum > ans1) ans1 = thissum;
}
//同理
thissum = 0;
for(i = mid + 1;i <= Right; ++i){
thissum += a[i];
if(thissum > ans2) ans2 = thissum;
}
return max(max(MaxSubSequenceSum(a, Left,mid), MaxSubSequenceSum(a, mid + 1,Right)), ans1 + ans2);
}
方法四 动态规划
其实在暴力的优化的时候就已经用过了
就是利用之前的信息进行更新
而不是每次进行重复累加
int MaxSubSequenceSum(int a[],int Left,int Right){
int ans = 0;
int thissum = 0;
for(int i = Left;i < Right + 1; ++i ){
thissum += a[i];
if(thissum < 0){
thissum = 0;//之前子段贡献为负数可以舍弃了
}
else if(thissum > ans){
ans = thissum;//取最大值
}
}
return ans;
}