动态规划系列之二最大和子数组
  KRe60ogUm4le 2024年03月22日 24 0

动态规划之最大和子数组

给定一个整数数组 nums ,找到一个具有最大和的子数组(子数组最少包含一个元素),返回其最大和

输入: [-2,-1,-3,4,-1,2,1,-5]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

解题思路

使用动态方程解题就需要题目符合动态方程解法,动态方程的两个思路:

  1. 将大的问题拆解成小一点问题,小问题和大问题的解决思路是类似的
  2. 小问题之间的关系能完成大问题的最优解

建立数学模型

求数组arr中最大和的子数组,建立一个子数组和的列表dp。其中dp[i]代表着以第i-1个元素结尾的数组的最大和。

一般情况下,我们可以用dp[i]代表以第 i 个元素结尾的子数组的最值,而递推关系就是考虑和dp[i-1]之间的关系

边界值

dp[i]代表这以第i-1个元素结尾的子数组的最大和,所以dp[0]就是边界值。当数组只有一个元素时,该元素就是数组的最大和

状态转移方程

状态转移方程是最难写的,但是我们要迎难而上。下面分析一下该状态如何转移。

这里用dp[i]代表以第 i 个元素结尾的子数组的最大和,则max(dp[i])就是要求的最终结果,那么关键是如何写出递推关系式。

转化后模型:
求i=7结尾的最大和。求i=7时,要知道前面的前一个dp[7-1]的值。比较 dp[6] + nums[7] 和 nums[7] 的大小,大的那一个就是dp[7]的值,也就是以 第7个元素结尾的子数组的最大值。

这里求8个元素的最大和的子数组,这个大问题可以拆解成小问题,即求8个元素的最大和的子数组,继续拆解可以为求7个元素的最大和的子数组,一直到求1个元素的最大和子数组。

-2,-1,-3,4,-1,2,1,-5
-2,-1,-3,4,-1,2,1
-2,-1,-3,4,-1,2
-2,-1,-3,4,-1
-2,-1,-3,4
-2,-1,-3
-2,-1
-2

小问题之间的联系,可以完成大问题的最优解。

-2 最大和就是-2
-2,-1 以-1结尾的最大和为-1,因为前i-1个元素+i元素的最大值为负数,所以最大值为当前-1
-2,-1,-3 以-3结尾的最大和为-3,前2个元素+i元素的最大值为-3,相比较当前值-3,最大值就是-3
-2,-1,-3,4 以4结尾的最大和为4,前3个元素的最大和-3为负数,但没第4个元素,所以最大的和为4
-2,-1,-3,4,-1 以-1结尾的最大和为3,前4个元素的最大和为4,所以加上当前值能变大,最大和为3
-2,-1,-3,4,-1,2 以2结尾的最大和为5,前5个元素的最大和为3,所以加上当前值能变大,最大和为5
-2,-1,-3,4,-1,2,1 以1结尾的最大和为1,前6个元素的最大和为5,加上当前值为6,
-2,-1,-3,4,-1,2,1,-5 以-5结尾的最大和1,前7个元素的最大和为1,大于0,所以加上当前元素能变大,最大和为1

统计出所有以第i个元素结尾的最大和为

-2 -1 -3 4 -1 2 1 -5
-2 -1 -3 4 3 5 6 1

可以看出当以1结尾时,可以获得最大和的子数组,值为6

明显的,因为我们考虑的子数组以nums[i]结尾,那么该数组一定是包含nums[i]这个元素的,因此需要考虑两种情况:即nums[i]单独成为一段还是与前面的dp[i-1]一起构成子数组,因此,可以得到如下的递推关系式:

dp[i]=max(dp[i-1]+nums[i],nums[i])

或者说:
dp[i-1]代表着前i-1个元素组成的子数组的最大和,那么加上第i个元素时就有两种情况:

  1. 第i个元素 大于 dp[i-1]+arr[i],那么前i个元素的最大值为第i个元素的值
  2. 第i个元素 小于 dp[i-1]+arr[i],那么前i个元素的最大值为前i-1个元素的值 + 第i个元素
dp[i] = max{arr[i], dp[i-1]+arr[i]}

python代码

input_list = [-2,-1,-3,4,-1,2,1,-5]
length = len(input_list)
dp = [0] * length

# 边界值,如果只有一个元素,则第一个元素的最大值就是自身
dp[0] = input_list[0]

for i in range(1,len(input_list)):
    # if dp[i-1] + input_list[i] > input_list[i] ---> dp[i-1] > 0。或许前一种写法更容易理解
    if dp[i-1] > 0:
        # 如果前i-1个元素的最大值大于0,那么加上当前就能使得以当前值为结尾的最大和变大
        dp[i] = dp[i-1] + input_list[i]
    else:
        # 如果前i-1个元素的最大值小于0,那么加上当前就能使得以当前值为结尾的最大和变小。以当前值为结尾的最大和就是自身组成的数组
        dp[i] = input_list[i]

print(dp)
print(max(dp))

又可以写成:

input_list = [-2,1,-3,4,-1,2,1,-5,4]

length = len(input_list)

# 构建一个dp数组,用来存储以第i个元素为结尾的最大子数组之和
dp = [0] * length
# 边界值,如果只有一个元素,则第一个元素的最大值就是自身
dp[0] = input_list[0]

for i in range(1,length):
    # 循环数组,比较前i-1个数组最大值+自身 和自身的大小。
    # 如果大则表明前i-1个数组是正数,则可以继续加上第i个
    # 如果小则表明前i-1个数组是负数,那么相加肯定更小,所以以自身为新起点继续往下走
    dp[i] = max(dp[i-1]+input_list[i],input_list[i])
    
print(max(dp))
     

小结

最长连续子序的解法精华在于 维护了一个dp数组,该数组中的每一个元素都代表着前i-1个元素能够组成的最大值,只需要比较前i-1个元素+第i个元素的值与第i个元素的值的大小,就能得到第前i个元素能够组成的子数组的最大值。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2024年03月22日 0

暂无评论

推荐阅读
  KRe60ogUm4le   2024年03月22日   32   0   0 linux算法
  KRe60ogUm4le   24小时前   6   0   0 递归算法
KRe60ogUm4le