最大字段和的分治算法
,对一个数组求其最大子段和,可采用分治思想将其一分为二,例如给定所求为a[n],就可将所给序列分成a[0:n/2] 和 a[n/2 + 1:n]两个部分,则最大值有一下三种情况:
①整个序列的子段和在左半部分
②整个序列的子段和在右半部分
③整个序列的子段和在两个部分的中间连接部分
通过对maxsum函数进行递归可求出前两种情况下的最大子段和,而针对第三种情况,则需要特殊处理,从mid所指位置分两部分向两边搜索,将两部分找到收到的最大和相加,最后对三种情况下算出字段和进行对比,选出其中最大值即为所求最大子段和。
一、关键算法伪代码描述
int maxsum(int *a,int left,int right)
{
if(left==right) return a[left];//当长度为1时直接返回
mid=(left+right)/2;//二分求中间值
lsum=maxsum(a,left,mid);//对左半部分递归求子段和
rsum=maxsum(a,mid+1,right);//对右半部分递归求子段和
for(int i=mid;i>=left;i--)
{
temp+=a[i];
if(temp>=s1) s1=temp;//比当前子段和大就赋值
}
temp=0;
for(int i=mid+1;i<=right;i++)
{
temp+=a[i];
if(temp>=s2) s2=temp;
}//两部分操作相同
msum=s1+s2;//合并两部分和
return max(max(lsum,rsum),msum);//比较三者大小
}
注:max函数为求两者中较大值。
二、整体代码展示
#include<iostream>
using namespace std;
int max(int x,int y)
{
return x>y?x:y;
}
int maxsum(int *a,int left,int right)
{
if(left==right) return a[left];
int mid=(left+right)/2;
int lsum=maxsum(a,left,mid);
int rsum=maxsum(a,mid+1,right);
int s1(0),s2(0),temp(0),msum;
for(int i=mid;i>=left;i--)
{
temp+=a[i];
if(temp>=s1) s1=temp;
}
temp=0;
for(int i=mid+1;i<=right;i++)
{
temp+=a[i];
if(temp>=s2) s2=temp;
}
msum=s1+s2;
return max(max(lsum,rsum),msum);
}
int main()
{
int n;
cin>>n;
int *a = new int[n];
for(int i=0;i<n;i++)
{
cin>>a[i];
}
cout<<maxsum(a,0,n-1);
return 0;
}
三、算法时间复杂度
分解子问题时间复杂度为O(1)
求解子问题时间复杂度为2T(n/2)
合并子问题算法时间复杂度为O(n)
由主定理得:
T(n)=2T(n/2)+O(n) ~~~> O(nlogn)
主定理:
四、体会与思考
1.递归与分治法
分治法的思想就是将大问题分成小份,一份一份解决最后合并解得出答案,其与递归方法紧密相连,在用递归进行分治法解决问题时,要注意问题规模最小时的情况,可用与做退出判断,递归过程中要清楚具体操作的是哪部分数据,捋清递归顺序。
2.如何正确分治
在例子大整数乘法中,通过式子形式转换,减少了一个要求的子问题,对算法时间复杂度有所提升,所以在划分子问题时尽可能找到最合理的划分方法,而且一定要确定所研究的问题再分成若干解后对算法复杂度有所提升,且小解合并与原解相同,才考虑使用分治法。