题目在这:https://leetcode-cn.com/problems/maximum-subarray/
思路分析:
一开始想用滑动窗口做这道题,后来一想,排序过后顺序不就乱套了,就没法找连续子数组了。
于是使用动态规划。
看了一个大佬的思路,很不错。
例如,示例 1 输入数组是 [-2,1,-3,4,-1,2,1,-5,4] ,我们可以求出以下子问题:
子问题 1:经过 -2−的连续子数组的最大和是多少;
子问题 2:经过 1 的连续子数组的最大和是多少;
子问题 3:经过 -3 的连续子数组的最大和是多少;
子问题 4:经过 4 的连续子数组的最大和是多少;
子问题 5:经过 -1 的连续子数组的最大和是多少;
子问题 6:经过 2 的连续子数组的最大和是多少;
子问题 7:经过 1的连续子数组的最大和是多少;
子问题 8:经过 -5 的连续子数组的最大和是多少;
子问题 9:经过 4 的连续子数组的最大和是多少。
一共 9 个子问题,这些子问题之间的联系并没有那么好看出来
例如「子问题 3」:经过 -3 的连续子数组的最大和是多少。
「经过 -3 的连续子数组」我们任意举出几个:
[-2,1,-3,4] ,-3是这个连续子数组的第 3 个元素;
[1,-3,4,-1] ,-3 是这个连续子数组的第 2 个元素;
……
我们不确定的是:-3−是连续子数组的第几个元素。那么我们就把 -3 定义成连续子数组的最后一个元素。在新的定义下,我们列出子问题如下:
子问题 1:以 -2 结尾的连续子数组的最大和是多少;
子问题 2:以 1 结尾的连续子数组的最大和是多少;
子问题 3:以 −3 结尾的连续子数组的最大和是多少;
子问题 4:以 4 结尾的连续子数组的最大和是多少;
子问题 5:以 -1结尾的连续子数组的最大和是多少;
子问题 6:以 2 结尾的连续子数组的最大和是多少;
子问题 7:以 1 结尾的连续子数组的最大和是多少;
子问题 8:以 -5 结尾的连续子数组的最大和是多少;
子问题 9:以 4 结尾的连续子数组的最大和是多少。
我们加上了「结尾的」,这些子问题之间就有了联系。我们单独看子问题 1 和子问题 2:
子问题 1:以 -2 结尾的连续子数组的最大和是多少;
以 -2 结尾的连续子数组是 [-2],因此最大和就是 −2。
子问题 2:以 1 结尾的连续子数组的最大和是多少;
以 1 结尾的连续子数组有 [-2,1] 和 [1] ,其中 [-2,1] 就是在「子问题 1」的后面加上 1 得到。-2 + 1 = -1 < 1−2+1=−1<1 ,因此「子问题 2」 的答案是 1。
大家发现了吗,如果编号为 i 的子问题的结果是负数或者 0 ,那么编号为 i + 1 的子问题就可以把编号为 i 的子问题的结果舍弃掉(这里 i 为整数,最小值为 1 ,最大值为 8),这是因为:
一个数 a 加上负数的结果比 a 更小;
一个数 a 加上 0 的结果不会比 a 更大;
而子问题的定义必须以一个数结尾,因此如果子问题 i 的结果是负数或者 0,那么子问题 i + 1 的答案就是以 nums[i] 结尾的那个数。
得到状态转移方程:
- dp[i] = dp[i-1] + num[i] 当 dp[i-1] >0
- dp[i] = num[i] 当 dp[i-1] <= 0
完整代码:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
length = len(nums)
dp = length *[0]
dp[0] = nums[0]
for i in range(1,length):
if dp[i-1] > 0:
dp[i] = dp[i-1] +nums[i]
else:
dp[i] = nums[i]
print(max(dp))
return max(dp)
我们每次只需要得到 dp【i】 的最大值即可
所以进一步压缩状态:
完整代码:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
res = nums[0]
a = nums[0]
for i in range(1,len(nums)):
if a > 0:
b = a + nums[i]
res = max(b,res)
a = b
else:
a = nums[i]
res = max(a,res)
print(res)
return res**