问题描述:给定由n个整数组成的序列a1,a2,…an,试着找出一个连续字段和,使得该子段和值最大。
例如:-1,11,-4,13,-5,-2的最大字段和为20(11,-4,13)
枚举法:
假如将序列的所有值存储在数组当中,枚举思想就是依次遍历数组,确定目标字段的起点下表i,再从该起点到数组末尾确定终止下表j,计算出从起点i到终点j的累加和,找出最大值为止,代码如下,时间复杂度为n的平方数量级。
int Maxsum_1(int*a,int n)
{ int i,j,sum;
int max=0;
for(i=0;i<n;i++){//起点下标
sum=0;
for(j=i;j<n;j++){//终点下标
sum+=a[j];//起点不变,终点后移累加
if(sum>max) max=sum;
}
}
return(max);
}
分治法:
- 分治基本思想:将难以分析解决的大问题,分解成较小规模的相同的子问题,分而治之,再利用子问题的解求出原问题的解。
- 分治思想的典型应用:最大子段和问题。
- 最大子段和的分治思想:将数组a[1…n]分解成a[1…n/2]和a[n/2+1…n]两部分,
则a[1…n]最大子段和出现三种情况:
1.a[1…n]最大子段和出现在a[0…n/2-1]中
2.a[1…n]最大子段和出现在a[n/2…n-1]中
3.a[1…n]最大子段和为包括a[n/2]的中间部分,a[i]+a[i+1]+…a[j], i<=n/2, n/2+1<=j<n。
代码如下,时间复杂度O(nlogn).
int Maxsum_2(int*a,int begin,int end){//分治 :将大问题分成几个独立的小问题
int sum=0,lsum,rsum,s1=0,s2=0;
int i,left=0,right=0;
int mid=(begin+end)/2;//只有一个元素情况
if(begin==end) sum=(a[begin]>0)?a[begin]:0;
else{
lsum=Maxsum_2(a,begin,mid);//第一种情况 左半区间最大子段和
rsum=Maxsum_2(a,mid+1,end);//第二种情况 右半区间最大子段和
//第三种情况
for(i=mid;i>=begin;i--){//以中位元素为起点向右遍历,找部分最大子段
left+=a[i];
if(left>s1) s1=left;
}
for(i=mid+1;i<=end;i++){//以中位元素为起点向左遍历,找部分最大子段
right+=a[i];
if(right>s2) s2=right;
}
//找出三种情况的最大值
sum=s1+s2;
if(sum<lsum) sum=lsum;
if(sum<rsum) sum=rsum;
}
return sum;
}
后言
作者小白会不断更新,发布一些自己所学习过程中的所思所想,其中会涉及到经典编程题,java或c++语言的知识总结,希望对正在阅读的你会有所帮助与启发,也欢迎大佬们评论指出错误和提出意见哦,一起进步吧。