【LeetCode】剑指DP:53. Maximum Subarray 最大子串和

一、概述

Easy题,吉跋猫Easy。这题我想了一个半小时没想出,只好投降。看了半天看明白了DP的原理,还有递归和分治,这俩懒得看了。服了服了,先看DP吧。实在是有点难。

Discuss区一个老哥的抱怨是我内心的真实写照:

hhhhh还是自己太菜了。

二、分析

我最开始看:Easy题么,应该不难。美滋滋开始分析递推关系:

嗯,假设我现在输入的是123,它是和最大的子串,下一个,下一个要是正数,就放里,不是正数,就不放里。

这不就是递推公式嘛。是个屁,假设下一个是-1,下下一个是3,那么怎么办?-1要放里,不放不是根据正负来的。

正的一定放里,负的可能放里可能不放。所以我这算个屁的递推关系。

那找不到递推关系。这时怎么办?

自己手写一下所有的结果。

如下:

来看一下这些值都是怎么算出来的:

-2,第一个,所以最大子串和为-2;

1,我把-2和1比较,1大,所以最大子串和为1;

-3,1-3=-2,-2小于1,所以最大子串和还是1;

4,1-3+4=2,2大于1,最大子串和是2么?不是,4自己更大,最大子串和为4;

-1,4-1=3,3小于4,所以最大子串和还是4;

2,4-1+2=5,5大于4,最大子串和为5;

1,4-1+2+1=6,6大于5,最大子串和为6;

-5,4-1+2+1-5=1,1小于6,所以最大子串和还是6;

4,4-1+2+1-5+4=5,5小于6,所以最大子串和还是6。

输出6。

好像有点眉目了。看上面可以看出一个规律:好像把原来的数组分成了若干个小数组。为什么这样呢?

我们来想一下,对于一个数组,找一个子数组,要求左端固定,其和在所有子数组中最大。怎么找?既然左端固定,那就遍历原数组,一个一个加入子数组里面,加完之后看和是否变大,维护一个变量储存最大值就可以。也就是说,对于一个左端固定的子数组,找和最大子数组很简单。

现在的问题是,左端不是固定的,它可以随意变——真的可以随意变么?我们再来观察上面推出结果的过程,子数组的左端变化过程为-2→1→4,最左端就变了三次。在最左端不变时,问题就退化成了上面的问题。

因此问题变成了最左端什么时候会变?-2,1,4,这些数字有什么共性?

-2组成的数组是第一个,它钦定是最左端的,不用看它。

为什么1能代替-2成为最左端的呢?因为1加上它前面的-2比1小啊。加还不如不加,那就不加,只留下1。

还是不清楚。再看4,为什么4能代替1呢?因为“1-3+4=2”,而2又比4小,我们换一下,4加上前面的1-3比4小啊。加还不如不加,那就不加,只留下4。

有点眉目了,是不是“对于一个正数,如果它前面的子串和小于0,那么‘加还不如不加’,然后左端点就移动到新的正数”,是这样么?

如果上面说的是对的,那么将原数组分割成多个小数组的分隔点,就是满足上面条件的正数了。

嗯?存不存在这样一种情况:比如说我的部分子串是1和10,现在到10了,发现前面子串和为负数,所以从10开始,但是实际上从10左边的1开始更好?

不存在。为什么呢?因为1也是一个正数啊,从10开始的前提是前面子串和为负数,那么对于1来说,它的前面子串和也是负数,因此在1的时候,左端点就移动到1了,对于10来说,不存在前面是1而且前面子串和是负数的情况。

然后在子串里,在左端点没有移动的时候,就加上一个元素,更新max,最后最终的max就是结果。

我觉得这不是一道典型的DP题,它没有直观的递推公式,或者需要这样说,它的递推公式不能直接求出结果,求出结果还需要另外一步。

代码如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int tmp=nums[0];
        int res=tmp;
        for(int i=1;i<nums.size();++i)
        {
            tmp=nums[i]+(tmp>0?tmp:0);
            res=max(res,tmp);
        }
        return res;
    }
};

tmp负责更新当前子串的和,当遇到一个正数的时候,若当前子串的和为负数,tmp变为这个正数【这个操作等价于子数组的左端点改变】;若当前子串的和为正数,那么直接加进去;如果遇到负数,那么直接加进去。然后更新tmp的最大值。所以DP是针对“当前子串的和”进行DP,不是针对“子串的最大和”进行DP。

三、总结

算是我写的最难的Easy题了。

这道题教会了我做DP的一个思路:如果DP没法直接求出结果,而且这个结果是一个最值,那么可以用DP求这个结果的所有值,每次求完之后更新最值。

更重要的,如果对一道题没思路,就手写出自己做出结果的整个过程,算法就隐藏在过程里。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值