首先声明一下,我没有卖焦虑,是我本身焦虑了。其次,要感谢个人师傅,西湖区最帅.....前端
好了,言归正传,在leetCode评论区你均可以看到 lucifer,简称路西法大佬;他的题解才是符合人类思惟的思考方式,思考的点都是很深刻浅出的。git
为何要刷算法呢?由于如今大前端时代,而前端开发确实是比其它领域要稍微简单一点的,注意,我并无说前端领域简单,是入门相对简单。入门简单,就意味着入门的人会很是的多,那怎么在众多的初级前端中脱颖而出呢?我选择了刷算法,刷算法有如下几点好处。算法
你说的我都会,我也能看得懂,为何一说/看就会,一写就费呢?数组
由于大多数题解,甚至是leetcode评论区的题解,只会告诉答案,不会告诉你思考答案的方式。或者有些会告诉你怎么思考,可是他们都是经验丰富的dp选手,思考方式彻底不适合新手,一个动态转移方程莫名其妙的就出来了???满脸的黑人问号,我曾经也是这样走过来的,再次感谢路西法大佬的指点。markdown
到底咱们要以什么方式来学习动态规划呢?个人建议是硬着头皮先刷点简单的,层层递进,再刷点困难的;刷题的过程当中千万不能浮躁,不要为了AC而AC,而是要经过本身耐心的观察,抽象,练习,概括总结;递归直至理解dp,熟悉dp。真的没有什么快捷的方式,若是有,都是骗人的。这里,结合实战来带你们过一遍,用符合人类的思考方式彻底扒开动态规划的裤子,学习(gandiao)动态规划。ide
若是你不熟悉动态规划,或者彻底不了解,我建议你先看看我上一篇 动态规划 的文章。oop
好了,咱们直接上菜,先来看看 题目 ;post
不要多想,咱们先以符合人类最简单的思惟方式暴力求解,再根据状态树考虑如下两点。学习
那这题怎么暴力呢?直接枚举nums,以nums[i]为起点,不断的加到最后一位,加的过程当中维护一个最大值便可,我写下代码。千万不要看不起暴力求解,是dp的突破口!ui
咱们来看下这个暴力的状态树,我只画出前面最长的两个分支,其它自行脑补。
分析一下这个暴力状态树两个分支,很明显答案都是是 [4,-1,2,1] ,后一个分支比前一个分支少一个-2,也就是问题的规模变小了,答案依然是最优的,这就存在最优子结构。
咱们在算第二个分支的时候,其实前面第一个分支已经算过了,这就是重复子问题。
通常这种最值型,都比较适合dp来求解;如何能快速的用dp求解,就靠你本身去攒经验了。
状态定义的对不对,直接是决定了你的dp方程式对不对,从而决定了你dp的方式对不对;
在【定义状态】以前,咱们先要搞清楚两个东西,一个是【状态】,一个是【选择】;
注意:这里的【状态】和【定义状态】是两个东西,【状态】是题目给出的条件,会影响结果的条件,而定义【定义状态】是为了明确dp方程式的含义,以便后面根据状态的变化,利用数学概括法得出状态转移方程,说白了就是找规律。
举个例子,x + y = z,你必需要明确你的x,y,z是啥,你才能写出这样的状态转移方程;
到这里,咱们明确了【状态】和【选择】,而定义好转移方程的状态;咱们必需要有如下两个意识。
以题目为例,nums = [-2,1,-3,4,-1,2,1,-5,4],最后一步是什么?最优策略中使用的最后一个选择是什么?很明显这道题的最后一个选择是nums[6] = 1,若是【不选择】1,上面咱们说过了,就是另起炉灶;若是选择1,答案就是最终的 [4,-1,2,1];
根据以上的两点,结合明确的两个东西,一个是【状态】,另外一个【选择】
到这里,状态的定义咱们就能够很明确的得出来了。
dp[i] = x,表示以 nums[i] 结尾的最大子序和为x; i < nums.length
必定要想清楚状态的定义,状态的定义直接是决定了你的转移方程对不对,dp的姿式对不对;
根据上面状态的定义分析,咱们来找下规律(数学概括法);nums[i] 是一个个的状态;对于每一个状态,咱们能够选择,或者不选择,若是选择以nums[i]为结果,那答案就是 nums[i] + dp[i-1],由于要连续,全部得加上前面的;若是不选择,就是另起炉灶,以nums[i]开头;枚举全部的状态,取两种选择的最大值,不就是答案了吗?这就是状态转移方程了;
dp[i] = max(nums[i],dp[i - 1] + nums[i]),i > 1
由于咱们枚举全部的状态,取两种选择的最大值就是答案,因此边界就是数组长度;初始值是什么呢?很明显就是数组自己,即dp[i] = nums[i];
必定要明确上面的状态定义,转移方程,边界和初始值才开始写代码,有一点不明白都不能写代码,否则基本一写就费。想清楚了写代码也要很是细心。
咱们用一样的套路解决乘积最大子数组
暴力求解就不解释了,同上;
先来明确 【状态】 和 【选择】,这题一样的,状态就是一个个 nums[i],而对于每一个 nums[i] 状态,一样用是选或不选两个选择;可是这题有一个比较隐晦的条件,须要考虑进去,就是两个数相乘:
那咱们在枚举全部的状态时候,根据这两个条件,不断的维护最大最小值,遇到负数就乘以最小值,遇到正数就是乘以最大值便可。结果只和最大最小值有关系,那咱们枚举状态不断更新这两个值就能够求出答案了,其实若是是遇到0,状况也是同样的;
状态的定义:
初始条件就是第一个数nums[i]
这里直接给出代码了。QAQ
我觉的dp是最能体现代码功底的,为何呢?由于它难。
必定要多练习,看懂了只是我懂了,你要真懂必须多练;
另外推荐几个我以为写得还不错的文章和视频,没有打广告QAQ