1)首先最简单的就是模拟时间复杂度是O(n^3)
/*
最大连续和问题
O(n^3)
*/
#include<cstdio>
#include<iostream>
using namespace std;
int main() {
int a[1005],n,sum,ans;
while(~scanf("%d",&n)) {
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
ans=a[1];
int cnt=0;
for(int i=1; i<=n; i++)
for(int j=i; j<=n; j++) {
sum=0;
for(int k=i; k<=j; k++)
{
sum+=a[k];
cnt++;
}
ans=max(ans,sum);
}
printf("ans=%d cnt=%d\n",ans,cnt);
}
}
2)将上述的代码优化一点点,运用树状数组的思想求子序列的和为两个前缀和之差
时间复杂度为O(n^2)相对于上面的3次方的时间复杂度优化了不少。
/*
最大连续和,运用:子序列和等于两个前缀和之差
O(n^2)
*/
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
int a[1005],ans,n,s[1005];
while(~scanf("%d",&n)) {
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
s[0]=0;
ans=a[1];
for(int i=1; i<=n; i++)
s[i]=s[i-1]+a[i];
for(int i=1; i<=n; i++)
for(int j=i; j<=n; j++)
ans=max(ans,s[j]-s[i-1]);
printf("%d\n",ans);
}
}
3)接下来还可以将2次方是时间复杂度再次优化,那就是用分治法。
时间复杂度为O(nlogn),相比较而言又优化了很多
/*
分治法求最大连续和问题
分治法的三个步骤:
1)划分问题:把问题的实例化成子问题
2)递归求解:递归解决子问题
3)合并问题:合并子问题的解,得到原问题的解
O(nlogn)
*/
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[1005];
int maxsum(int *a,int x,int y) { //返回在数组中左闭右开[x,y)的最大连续和(空区间可表示为[x,x))
int v,l,r,maxs;
if(y-x==1) return a[x];//如果只有一个元素,直接返回;
int m=x+(y-x)/2;//第一步,划分成[x,m),[m,y);
//此处用x+(y-x)/2,而不用(x+y)/2,'/'总是朝0方向取整,而不是向下取整,此处的方式可以保证分界点总是靠近区间起点。
maxs=max(maxsum(a,x,m),maxsum(a,m,y));//第二步:递归求解子问题
//第三步:合并;
v=0,l=a[m-1];//从分界点开始往左的最大连续和l;
for(int i=m-1; i>=x; i--)
l=max(l,v+=a[i]);
v=0,r=a[m];//从分界点开始往右的最大连续和r;
for(int i=m; i<y; i++)
r=max(r,v+=a[i]);
return max(maxs,l+r);
}
int main() {
int n;
while(~scanf("%d",&n)) {
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
printf("%d\n",maxsum(a,1,n+1));
}
}