不同复杂度求解最大连续和

来源:互联网 发布:淘宝手机充值卡换现金 编辑:程序博客网 时间:2024/05/16 14:05

问题描述:
给定一个整数序列A[0],A[1], … , A[N-1],每个元素可正可负,求该序列中最大的连续和。

方法1 暴力求解法

这种方法最直接,暴力枚举每一个每一个连续片段的起点和终点,然后求相应
的和。其复杂度即为O(n3)
算法描述为:

int maxx = A[0];for(int i=0; i<N; i++)    for(int j=i; j<N; j++)    {        int sum = 0;        for(int k=i; k<=j; k++) sum+=A[k];        maxx = sum>maxx ? sum:maxx;    }

方法2 利用递推前缀和记录0i项的和

这种方法使用了已经计算的信息,减少了重复工作,使得复杂度降为O(n2)
算法描述为:

S[0] = A[0];maxx= A[0];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++)        maxx = maxx>S[j]-S[i-1] ? maxx:S[j]-S[i-1];

方法3 采用二分法进行递归求解,进一步降低复杂度

这种方法可以有效降低复杂度,其复杂度为O(nlongn)
代码如下:

// O(nlogn)复杂度int maxsum(vector<int>& A, int x, int y){    if(y-x==1)  return A[x];    int m = x+(y-x)/2;    int mx = maxsum(A, x, m);    int my = maxsum(A, m, y);    int maxx = mx>my ? mx:my;    int v = 0, L = A[m-1], R = A[m];    /// 从分界点开始往左的最大连续和L,并保证L不减    for(int i=m-1; i>=x; i--)   L = L>=(v+=A[i]) ? L:v;    v = 0;    /// 从接点点开始往右的最大连续和R,并保证R不减    for(int i=m; i<y; i++)  R = R>=(v+=A[i]) ? R:v;    return maxx>(L+R) ? maxx:L+R;}

方法4 优化方法2,并使复杂度变为线性

这种方法同样利用了前缀和,并且用mx记录前i项中最大的连续和,在一次遍历A的过程中同时更新mx和sum,从而使得复杂度O(n)
代码如下:

// O(n)复杂度int maxsum2(vector<int>& A){    int * sum = new int[A.size()];    int mx = A[0];  // or mx = INT_MIN;    sum[0] = A[0];    for(int i=1; i<A.size(); i++)  mx = mx>(sum[i]=sum[i-1]>0?sum[i-1]+A[i]:A[i]) ? mx:sum[i];    delete sum;    return mx;}

这4种方法中以最后两种比较好用,而最后一种在一次遍历中同时更新了多个量,达到了算法的最优。这启发我们,在算法设计过程中,尽量充分利用已经计算的信息可以使得算法更加优化。

后记:
本质上第4种方法是一种DP。
其中sum[i]用来记录a[0]…a[i]的最大连续和。即

sum[0] = a[0];sum[i] = max(a[i], a[i]+sum[i-1]);

如果sum[i-1]为负数,那么sum[i]=a[i],否则sum[i]=sum[i-1]+a[i];

参考http://blog.csdn.net/newmemory/article/details/50821573

1 0
原创粉丝点击