动态规划_最大子数组|||_1

1. 【问题描述】    最大子数组|||

2. 【思路】    参考:LintCode:Maximum Subarray III

假设给定数组共有N个元素,需要找到K个不重合的子数组使其和最大。令符号(i, j)表示在数组前i个元素中找到j个不重合的和最大的子数组的和,符号[a,b]表示在子数组nums[a,a+1,...,b]中找到一个最大子数组,返回最大子数组的和。则有:

(i, j)=max{ (q, j),(p, j-1)+[p+1, i], 其中j<=q<=i-1,j-1<=p<=i-1}

下面以N=6,K=4为例具体说明.首先构建二维数组dpk

(1,1)

(2,1)    (2,2)

(3,1)    (3,2)    (3,3)

                (4,2)    (4,3)    (4,4)

                                (5,3)    (5,4)

                                                (6,4)

(6,4)即为所求.根据(i, j)的表达式可以写出:

(1,1)=nums[0];

(2,2)=(1,1)+[2,2];

(3,2)=max{(2,2),(2,1)+[3,3],(1,1)+[2,3]};

(4,2)=max{(2,2),(3,2),(3,1)+[4,4],(2,1)+[3,4],(1,1)+[2,4]};

(3,3)=(2,2)+[3,3];

(4,3)=max{(3,3),(3,2)+[4,4],(2,2)+[3,4]};

(5,3)=max{(3,3),(4,3),(4,2)+[5,5],(3,2)+[4,5],(2,2)+[3,5]};

观察(2,2)与(3,2)的表达式可以发现:(2,2)=(1,1)+[2,2]<=(1,1)+[2,3],所以(3,2)的表达式可以改写为:

(3,2)=max{(2,1)+[3,3],(1,1)+[2,3]};

同理可以改写后面的表达式。基于此可以改写(i, j)的表达式:

(i, j)=max{ (p, j-1)+[p+1, i], 其中j-1<=p<=i-1}

观察上述(1,1)到(5,3)的表达式发现,加粗部分重复出现,所以可以用一个二维数组保存[a,b]的值避免重复计算.

下面的代码中,过程maxBE(nums,beg,end,dp1)计算[beg,end],其中二维数组dp1保存[beg,end]的值避免重复计算

3. 【代码】    

class Solution {
public:
    /**
     * @param nums: A list of integers
     * @param k: An integer denote to find k non-overlapping subarrays
     * @return: An integer denote the sum of max k non-overlapping subarrays
     */
     
    int maxBE(vector<int> &nums,int beg,int end,vector<vector<int>> &dp1) {
        if(dp1[beg+1][end+1]!=INT_MIN) {
            return dp1[beg+1][end+1];
        }
        if(beg==end) {
            dp1[beg+1][end+1]=nums[beg];
            return dp1[beg+1][end+1];
        }
        int mid=beg+(end-beg)/2;
        int i=mid-1,left=nums[mid],ls=left;
        while(i>=beg) {
            ls+=nums[i];
            left=left>=ls?left:ls;
            i--;
        }
        int j=mid+2,right=nums[mid+1],rs=right;
        while(j<=end) {
            rs+=nums[j];
            right=right>=rs?right:rs;
            j++;
        }
        int left_sum=maxBE(nums,beg,mid,dp1),mid_sum=left+right,right_sum=maxBE(nums,mid+1,end,dp1);
        dp1[beg+1][end+1]=(left_sum>=right_sum?left_sum:right_sum)>=mid_sum?(left_sum>=right_sum?left_sum:right_sum):mid_sum;
        return dp1[beg+1][end+1];
    }
    int maxSubArray(vector<int> nums, int k) {
        // write your code here
        int N=nums.size();
        if(N==0||k<=0||N<k) {
            return -1;
        }
        vector<vector<int>> dpk(N+1,vector<int>(k+1,INT_MIN));
        vector<vector<int>> dp1(N+1,vector<int>(N+1,INT_MIN));
        for(int i=1;i<=N+1-k;++i) {
            dpk[i][1]=maxBE(nums,0,i-1,dp1);
        }
        for(int j=2;j<=k;++j) {
            for(int i=j;i<=N+j-k;++i) {
                for(int p=j-1;p<=i-1;++p) {
                    dpk[i][j]=dpk[i][j]>=(dpk[p][j-1]+maxBE(nums,p,i-1,dp1))?dpk[i][j]:(dpk[p][j-1]+maxBE(nums,p,i-1,dp1));
                }//for
            }//for
        }//for
        return dpk[N][k];
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值