题目:给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
分析:见总结
代码:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
nums_len = len(nums)
if nums_len == 0:
return 0
if nums_len == 1:
return nums[0]
nums_list = [0 for _ in nums]
nums_list[0] = nums[0]
for i in range(1,nums_len):
if nums_list[i-1]>0:
nums_list[i] = nums_list[i-1] + nums[i]
else:
nums_list[i] = nums[i]
return max(nums_list)
【理解dp】
动态规划(英语:Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划不是某一种具体的算法,而是一种算法思想:若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。应用这种算法思想解决问题的可行性,对子问题与原问题的关系,以及子问题之间的关系这两方面有一些要求,它们分别对应了最优子结构和重复子问题。
最优子结构
最优子结构规定的是子问题与原问题的关系
动态规划要解决的都是一些问题的最优解,即从很多解决问题的方案中找到最优的一个。当我们在求一个问题最优解的时候,如果可以把这个问题分解成多个子问题,然后递归地找到每个子问题的最优解,最后通过一定的数学方法对各个子问题的最优解进行组合得出最终的结果。总结来说就是一个问题的最优解是由它的各个子问题的最优解决定的。将子问题的解进行组合可以得到原问题的解是动态规划可行性的关键。在解题中一般用状态转移方程描述这种组合。例如原问题的解为 f(n)f(n),其中 f(n)f(n) 也叫状态。状态转移方程 f(n) = f(n - 1) + f(n - 2)f(n)=f(n−1)+f(n−2) 描述了一种原问题与子问题的组合关系 。在原问题上有一些选择,不同选择可能对应不同的子问题或者不同的组合方式。例如
n = 2kn=2k 和 n = 2k + 1n=2k+1 对应了原问题 nn 上不同的选择,分别对应了不同的子问题和组合方式。
找到了最优子结构,也就能推导出一个状态转移方程 f(n)f(n),通过这个状态转移方程,我们能很快的写出问题的递归实现方法。
重复子问题
重复子问题规定的是子问题与子问题的关系。
当我们在递归地寻找每个子问题的最优解的时候,有可能会重复地遇到一些更小的子问题,而且这些子问题会重叠地出现在子问题里,出现这样的情况,会有很多重复的计算,动态规划可以保证每个重叠的子问题只会被求解一次。当重复的问题很多的时候,动态规划可以减少很多重复的计算。
重复子问题不是保证解的正确性必须的,但是如果递归求解子问题时,没有出现重复子问题,则没有必要用动态规划,直接普通的递归就可以了。
例如,斐波那契问题的状态转移方程 f(n) = f(n - 1) + f(n - 2)f(n)=f(n−1)+f(n−2)。在求 f(5)f(5) 时,需要先求子问题 f(4)f(4) 和 f(3)f(3),得到结果后再组合成原问题 f(5)f(5) 的解。递归地求 f(4)f(4) 时,又要先求子问题 f(3)f(3) 和 f(2)f(2) ,这里的 f(3)f(3) 与求 f(5)f(5) 时的子问题重复了。
解决动态规划问题的核心:找出子问题及其子问题与原问题的关系
找到了子问题以及子问题与原问题的关系,就可以递归地求解子问题了。但重叠的子问题使得直接递归会有很多重复计算,于是就想到记忆化递归法:若能事先确定子问题的范围就可以建表存储子问题的答案。
动态规划算法中关于最优子结构和重复子问题的理解的关键点:
证明问题的方案中包含一种选择,选择之后留下一个或多个子问题
设计子问题的递归描述方式
证明对原问题的最优解包括了对所有子问题的最优解
证明子问题是重叠的(这一步不是动态规划正确性必需的,但是如果子问题无重叠,则效率与一般递归是相同的)
回归题目:
这是一道典型的使用「动态规划」解决的问题,需要我们掌握动态规划问题设计状态的技巧,并且需要知道如何推导状态转移方程,最后再去优化空间。
动态规划的重点之一是定义状态方程,这道题目中我们可以将问题分解成若干个子问题:
子问题 1:以 -2−2 结尾的连续子数组的最大和是多少;
子问题 2:以 11 结尾的连续子数组的最大和是多少;
子问题 3:以 -3−3 结尾的连续子数组的最大和是多少;
子问题 4:以 44 结尾的连续子数组的最大和是多少;
子问题 5:以 -1−1 结尾的连续子数组的最大和是多少;
子问题 6:以 22 结尾的连续子数组的最大和是多少;
子问题 7:以 11 结尾的连续子数组的最大和是多少;
子问题 8:以 -5−5 结尾的连续子数组的最大和是多少;
子问题 9:以 44 结尾的连续子数组的最大和是多少。
我们可以知道对于子问题9是在子问题8的基础上加上44,如果子问题8的结果大于0,则前面的结果具有增益性,则对于子问题9的结果就是 dp[9] = dp[8] + nums[9]
否则dp[9] = dp[9]
因此: