笔记
1.动态规划介绍
常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。
2.动态规划模板步
(1) 确定动态规划状态
(2) 写出状态转移方程(画出状态转移表)
(3)考虑初始化条件
(4)考虑输出状态
(5)考虑对时间,空间复杂度的优化(Bonus)
练习
解题思路:
1.第一步:确定动态规划状态
对于这个问题,我们的状态dp[i]也是以nums[i]这个数结尾的最长递增子序列的长度
2. 第二步:写出状态转移方程
这个问题,我们需要分两种情况考虑,第一种情况是如果遍历到的数 nums[i] 后面一个数不是比他大或者前一个数不是比他小,也就是所谓的不是连续的递增,那么这个数列最长连续递增序列就是他本身,也就是长度为1。第二种情况就是如果满足有递增序列,就意味着当前状态只和前一个状态有关, dp[i] 只需要在前一个状态基础上加一就能得到当前最长连续递增序列的长度。总结起来,状态的转移方程可以写成dp[i]=dp[i-1]+1
3. 第三步:考虑初始化条件
这个题目的初始化状态就是一个一维的全为1的数组。
4. 第四步:考虑输出状态
这个问题输出条件也是求dp数组中最大的数。
5. 第五步:考虑是否可以优化
这个题目只需要一次遍历就能求出连续的序列,所以在时间上已经没有可以优化的余地了,空间上来看的话也是一维数组,并没有优化余地。
代码实现:
def findLengthOfLCIS(self, nums: List[int]) -> int:
if not nums:return 0 #判断边界条件
dp=[1]*len(nums) #初始化dp数组状态
#注意需要得到前一个数,所以从1开始遍历,否则会超出范围
for i in range(1,len(nums)):
if nums[i]>nums[i-1]:#根据题目所求得到状态转移方程
dp[i]=dp[i-1]+1
else:
dp[i]=1
return max(dp) #确定输出状态
总结:需要理清子序列和子数组(连续序列)的差别,前者明显比后者要复杂一点,因为前者是不连续的序列,后者是连续的序列,从复杂度来看也很清楚能看到即使穷举子序列也比穷举子数组要复杂很多。