leetcode 53

思路一:分治

1.对于序列a0… a n a_n an,要求整个序列的最大连续子序列和,我们可以采用分治的思想考虑,将序列较为平均的分成两部分,a0… a n / 2 a_{n/2} an/2 a n / 2 + 1 a_{n/2+1} an/2+1 a n a_n an,那么要求的连续最大子序列和可能是前半部分的最大子序列和leftmax、后半部分的最大子序列和rightmax以及跨越两段的最大连续子序列和middlemax。
2.我们对序列进行一般化,记要求序列为al… a r a_r ar对于跨越两段最大的子序列middlemax有一个特征,记m=(l+r)/2,那么该子序列的左半部分应该是以下标m结尾的连续子序列ai… a m a_m am(i>=l),右半部分应该是以下标m+1开始的连续子序列 a m + 1 . . . . . . . a j a_{m+1}.......a_j am+1.......aj(j<=r)。
3.该问题可以通过分治求解子问题leftmax和rightmax,然后合并过程中再计算middlemax,即可递归获得结果。
4.难点是如何求取middlemax:假设我们已经能够求取出正确的leftmax和rightmax,在计算完leftmax和rightmax后,仅仅是知道leftmax和rightmax是不足以求取middlemax的,因此我们需要在求解leftmax和rightmax过程中返回一些信息便于我们求解middlemax,middlemax=(left中以下标m结尾的最大子序列和)+(right中以下标m+1开头的最大子序列和)。
5.作为递归程序,在利用利用子程序返回信息的同时,程序本身也应该计算返回相同结构的信息供其父程序使用,因为我们求解middlemax需要使用到(left中以下标m结尾的最大子序列和)+(right中以下标m+1开头的最大子序列和),所以我们也需要返回本身以r结尾的最大子序列rsum和以及以l开头的最大子序列和lsum。同时还有本序列求解的最大子序列和msum。
6.我们来看这三个返回结果是否足以供当前父程序向上返回序列{left,max}的rsum、lsum和msum,显然,在求解lsum和rsum出现了问题,我们还需要知道left的序列总和ltsum以及right的序列总和rtsum,以求解当前父程序的lsum和rsum,相应的,当前父程序也需要返回当前求解序列的总和tsum。
7.综上,计算每个子问题都需要返回
以r结尾的最大子序列rsum、
以l开头的最大子序列和lsum、
求解序列的总和tsum
求解的最大子序列和msum
将其设计组合成一个结构体rdata
8. rdata.lsum=max(leftdata.lsum,leftdata.tsum+rightdata.lsum);
rdata.rsum=max(rightdata.tsum+leftdata.rsum,rightdata.rsum);
rdata.tsum=leftdata.tsum+rightdata.tsum;
rdata.msum=max(max(leftdata.msum,rightdata.msum),leftdata.rsum+rightdata.lsum);
9.最特殊情况l=r,rdata的四个值应该都为nums[l]

代码

class Solution {
public:
    struct data
    {
        int lsum;//以l作为左边界的最大值
        int rsum;//以r作为右边界的最大值
        int tsum;//区间总和
        int msum;//区间最大值
    };
    struct data getmax(vector<int>& nums,int l,int r)
    {
        struct data rdata;
        if(l==r)
        {
            rdata.lsum=nums[l];
            rdata.rsum=nums[l];
            rdata.tsum=nums[l];
            rdata.msum=nums[l];
            return rdata;
        }
        else
        {
            int m=(l+r)/2;
            struct data leftdata =getmax(nums,l,m);
            struct data rightdata=getmax(nums,m+1,r);
            rdata.lsum=max(leftdata.lsum,leftdata.tsum+rightdata.lsum);
            rdata.rsum=max(rightdata.tsum+leftdata.rsum,rightdata.rsum);
            rdata.tsum=leftdata.tsum+rightdata.tsum;
            rdata.msum=max(max(leftdata.msum,rightdata.msum),leftdata.rsum+rightdata.lsum);
            return rdata;
        }
    }
    int maxSubArray(vector<int>& nums) {
        return getmax(nums,0,nums.size()-1).msum;
    }
};

思路二:贪心算法

1.假设最优序列为 a l a_l al a r a_r ar,那么任取一个子序列 a l a_l al a i a_i ai(l<=i<=r),该子序列和应该大于0,否则该子序列只会导致 a l a_l al a r a_r ar总和变小。
2.我们从第一个元素开始遍历,维护一个希望序列和total,total是指一段序列 a t 1 a_{t1} at1 a t 2 a_{t2} at2的和,其任意一段子序列 a t 1 a_{t1} at1 a i a_{i} ai(l<=i<=t2)的和 t o t a l i total_i totali是大于0的。
以及维护一个最优解ans,ans初始值置为负无穷。
3.对于每一个a[i],如果大于0,则直接加入total当中;如果小于0,则需要考虑其加入是否会导致total小于等于0,如果是,则清空,total置为0;如果大于0,则判断更新ans的值:ans=max{ans,total+a[i],a[i]},以及total值的更新:total=total+a[i]。
4.如果序列全为负数或0,那么ans应该是最大的负数或0。

代码:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int ans=nums[0];
        int total=nums[0]>0?nums[0]:0;
        for(int i=1;i<nums.size();i++)
        {
            ans=nums[i]>ans?nums[i]:ans;
            if(nums[i]>0)
            {
                total+=nums[i];
                ans=total>ans?total:ans;
            }
            else if(nums[i]<0)
            {
                if(total+nums[i]<0)
                {
                    total=0;
                }
                else
                {
                    total+=nums[i];
                }
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值