zoukankan      html  css  js  c++  java
  • 动态规划系列之二最大和子数组

    动态规划之最大和子数组

    给定一个整数数组 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个元素能够组成的子数组的最大值。

  • 相关阅读:
    R 语言中的数据结构
    minimap2 长reads比对工具
    seqtk 一款快速处理fasta/fastq 文件的小程序
    Eclipse R语言开发环境搭建 StatET插件
    Windows 安装R
    HttpClient 发送请求和参数
    Java Base64编码和解码
    docker 安装
    docker
    inotify 监控文件系统操作
  • 原文地址:https://www.cnblogs.com/goldsunshine/p/13221668.html
Copyright © 2011-2022 走看看