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

问题描述:
给定一个整数序列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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值