连续子数组的最大和及其拓展

题目

输入一个整形数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n).


基本思想:

利用动态规划求解,递归公式如下,其中f(i)表示以第i个数字结尾的子数组的最大和。

这里写图片描述

公式意义如下:

如果当以第i-1个数字结尾的子数组中所有数字的和小于0,则如果把这个负数与第i个数累加,则得到的结果比第i个数本身还要小,那么这时应该更新f(i)为第i个数字本身,即pData[i]。反之,如果以第i-1个数字结尾的子数组中所有数字的和大于0,则f(i)应该更新为第i个数字加上第i-1个数字结尾的子数组中所有数字的和。这样扫一遍数组之后就可以得到max[f(i)]。

代码如下:

int FindGreatestSumOfSubArray(vector<int> array) {
        if(array.empty()) return 0;

        int nCurSum=array[0];
        int res=nCurSum;

        for(int i=1;i<array.size();++i)
        {
            nCurSum=(nCurSum>0)?array[i]+nCurSum:array[i];
            res=(nCurSum>res)?nCurSum:res;
        }
        return res;
    }

拓展1:最大和子矩阵

题目:

这里写图片描述

基本思想:

在上面求连续子数组的最大和时候,其实所求的解可以等价穷举了所有可能连续子数组后得出了最大的连续子数组的和。
1. 那么在本题中,我们可以将矩阵的每一列看成一个数(其实是一个数组),首先求出第一行数组的连续子数组的最大和,即[-90,48,78],然后把第二行[64,-40,64]分别按列累加到第一行得到数组[-26,8,142],然后求出该数组的最大累加和。依次类推,依次累加到最后一行。
2. 接下来我们对矩阵的每一行,重复第1步的过程,这样就可以得出子矩阵最大累加和。
3. 以上1,2步骤也就是相当于枚举了所有子矩阵的最大累加和。

代码如下:

int sumOfSubMatrix(vector<vector<int> > mat, int n) {
        // write code here
        if(n==0) return -1;
        int max=INT_MIN;
        int cur=0;
        int *s=new int[n];//累加数组
        for(int i=0;i<n;i++){
            for(int i=0;i<n;++i)
                s[i]=0;//数组清零
            for(int j=i;j<n;j++){
                cur=0;
                for(int k=0;k<n;++k){
                    s[k]+=mat[j][k];
                    cur+=s[k];
                    max=cur>max?cur:max;
                    cur=cur<0?0:cur;
                }
            }
        }
        delete []s;
        return max;
    }

拓展2:不相交数组的最大和

题目:

给定一个长度为N的整数数组a,求不重叠的两个子数组的和的最大值。

如a[6]={1, 2, -4, 3, 2, -5}。所取的子数组分别为{1,2},{3, 2}时,两个子数组的和最大,为3+5=8。


基本思想:

首先利用两个数组array1和array2,array1保存从0到第i个数字结尾的连续子数组的最大和, array2保存从第i个数字到结尾len-1的连续子数组的最大和。那么将数组分别从第i个数字为分割线把数组分割成arr[0..i]跟arr[i..len-1],那么array[i]+array[i+1]就是所求值。

代码如下:

int twoSubArrayMaxSum(vector<int> arr){
    if(arr.size()<=0)
        return 0;
    vector<int> rArray;
    rArray.reserve(arr.size());//这里只开了一个数组,array1跟array2的两个过程其实就是正逆过程,在结算array2的时候可以直接利用array1的结果

    int max=INT_MIN;
    int cur=0;

    for(int i=arr.size()-1;i>0;i--){
        cur+=arr[i];
        max=cur>max?cur:max;
        rArray[i]=max;
        cur=cur<0?0:cur;
    }

    int res=INT_MIN;
    max=INT_MIN;
    cur=0;

    for(int i=0;i<arr.size()-1;i++){
        cur+=arr[i];
        max=cur>max?cur:max;
        res=(res>(max+rArray[i+1]))?res:(max+rArray[i+1]);
        cur=cur<0?0:cur;
    }

    return res;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值