本文内容参考《算法竞赛入门经典第2版》220~223页
Q:给出一个长度为nnn的序列A1,A2,A3,⋯ ,AnA_{1},A_{2},A_{3},\cdots,A_{n}A1,A2,A3,⋯,An,求最大连续和。换句话说,要求找到1≤i≤j≤n1\leq i\leq j \leq n1≤i≤j≤n,使得Ai+Ai+1+⋯+AjA_{i}+A_{i+1}+\cdots+A_{j}Ai+Ai+1+⋯+Aj尽量大。
方法一:
暴力枚举,时间复杂度O(n3)O(n^{3})O(n3)
ans=A[1];//所求的最大连续和
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){//枚举区间[i,j]
int sum=0;
for(int k=i;k<=j;k++){//对区间[i,j]内的所有数求和
sum+=A[k];
}
ans=max(sum,ans);//取最大值
}
}
方法二:
利用前缀和,时间复杂度O(n2)O(n^{2})O(n2)
s[0]=0;
for(int i=1;i<=n;i++){//提前求前缀和(s[i]表示从A[i]数组从1到i的所有数之和),降低时间复杂度
s[i]=s[i-1]+A[i];
}
ans=s[0];
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
ans=max(ans,s[j]-s[i-1]);
}
}
方法三:
分治法,时间复杂度O(nlogn)O(nlogn)O(nlogn)
int maxSum(int*A,int x,int y){//返回数组在左闭右开区间[x,y)中的最大连续和
if(y-x==1){
return A[x];
}
int m=x+(y-x)/2;//划分成[x,m)和[m,y)
int maxs=max(maxSum(A,x,m),maxSum(A,m,y));
int v,L,R;
v=0;
L=A[m-1];
for(int i=m-1;i>=x;i--){//从分界点往左的最大连续和
L=max(L,v+=A[i]);
}
v=0;
R=A[m];
for(int i=m;i<y;i++){//从分界点往右的最大连续和
R=max(R,v+=A[i]);
}
return max(maxs,L+R);//子问题的解与L和R比较
}