• 详谈动态规划问题并解最大子数组和


    今天刷力扣又学会了一种算法----动态规划,经过我查阅不少资料后,这些我总结的分享给大家

    动态规划是什么?

    动态规划(Dynamic Programming)是一种求解最优化问题的数学方法,它通常用于解决具有重叠子问题和最优子结构性质的问题。动态规划的基本思想是将原问题拆解成若干个子问题,然后通过求解子问题的最优解来得到原问题的最优解。

    动态规划有两个关键概念:重叠子问题和最优子结构。

    1. 重叠子问题: 在动态规划中,原问题可以被拆解成多个具有相同结构的子问题,而这些子问题可能会被多次求解。动态规划通过存储已解决的子问题的解,避免重复计算,提高效率。

    2. 最优子结构: 问题的最优解可以通过子问题的最优解来构造。也就是说,问题的整体最优解是由各个子问题的最优解组合而成的。这使得我们可以通过求解子问题来逐步推导出原问题的最优解。

    动态规划算法的一般步骤如下:

    1. 定义状态: 描述问题局部最优解的变量,这些变量可以表示问题的不同方面。

    2. 找到状态转移方程: 定义各个状态之间的关系,即问题的最优解如何由子问题的最优解推导而来。

    3. 初始化: 给定问题的初始状态,通常是问题规模较小时的情况,然后逐步扩展规模。

    4. 递推计算: 利用状态转移方程,从初始状态开始逐步计算得到问题的最优解。

    5. 解决原问题: 根据计算得到的最优解,得到原问题的最优解。

    动态规划常被用于解决许多问题,如最短路径问题、背包问题、编辑距离问题等。这种方法在解决那些可以被拆解成子问题,并且这些子问题存在重叠的情况下特别有效。

    如果觉得以上说法不是太通俗理解我们可以一边结合题,一边来详解什么是动态规划!

    使用动态规划求解最大子数组和

    先给出一个算法题(摘自力扣)

    给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    子数组 是数组中的一个连续部分。

    示例 1:

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

    示例 2:

    输入:nums = [1]
    输出:1
    

    示例 3:

    输入:nums = [5,4,-1,7,8]
    输出:23
    

    提示:

    • 1 <= nums.length <= 105
    • -104 <= nums[i] <= 104

     理解题意:

    题目要我们找出和最大的连续子数组的值是多少,「连续」是关键字,连续很重要,不是子序列。

    题目只要求返回结果,不要求得到最大的连续子数组是哪一个。这样的问题通常可以使用「动态规划」解决。

    如何定义子问题(定义状态)

    我们 不知道和最大的连续子数组一定会选哪一个数,那么我们可以求出 所有 经过输入数组的某一个数的连续子数组的最大和。

    经过分析,我们列出子问题如下:

    子问题 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。

    大家发现了吗,如果编号为 i 的子问题的结果是负数或者 0 ,那么编号为 i + 1 的子问题就可以把编号为 i 的子问题的结果舍弃掉(这里 i 为整数,最小值为 1 ,最大值为 8),这是因为:

    一个数 a 加上负数的结果比 a 更小;
    一个数 a 加上 0的结果不会比 a 更大;
    而子问题的定义必须以一个数结尾,因此如果子问题 i 的结果是负数或者 0,那么子问题 i + 1 的答案就是以 nums[i] 结尾的那个数。

    定义状态(定义子问题)

    dp[i]:表示以 nums[i] 结尾的连续子数组的最大和。

    「结尾」和「连续」是关键字。

    状态转移方程(描述子问题之间的联系)
    根据状态的定义,由于 nums[i] 一定会被选取,并且以 nums[i] 结尾的连续子数组与以 nums[i - 1] 结尾的连续子数组只相差一个元素 nums[i] 。

    假设数组 nums 的值全都严格大于 0,那么一定有 dp[i] = dp[i - 1] + nums[i]。

    可是 dp[i - 1] 有可能是负数,于是分类讨论:

    如果 dp[i - 1] > 0,那么可以把 nums[i] 直接接在 dp[i - 1] 表示的那个数组的后面,得到和更大的连续子数组;
    如果 dp[i - 1] <= 0,那么 nums[i] 加上前面的数 dp[i - 1] 以后值不会变大。于是 dp[i] 「另起炉灶」,此时单独的一个 nums[i] 的值,就是 dp[i]。
    以上两种情况的最大值就是 dp[i] 的值,写出如下状态转移方程:

    dp[i]=\left\{\begin{matrix} dp[i-1]+nums[i],if & dp[i-1]>0 & & \\ nums[i], if &dp[i-1]\leqslant 0 \end{matrix}\right.

    输出:

    注意:这里状态的定义不是题目中的问题的定义,不能直接将最后一个状态返回回去

    我们需要将之前的dp全部遍历一遍,然后取最大值后返回

    算法实现代码

    1. class Solution {
    2. public int maxSubArray(int[] nums) {
    3. int len = nums.length;
    4. int [] dp=new int[len];
    5. dp[0]=nums[0];
    6. for(int i=1;i
    7. if(dp[i-1]<0){
    8. dp[i]=nums[i];
    9. }else{
    10. dp[i]=dp[i-1]+nums[i];
    11. }
    12. }
    13. int res=dp[0];
    14. for(int i=1;i
    15. res=Math.max(res,dp[i]);
    16. }
    17. return res;
    18. }
    19. }

    算法思路转载来源:力扣(LeetCode)

    作者:liweiwei1419
    链接:https://leetcode.cn/problems/maximum-subarray/

  • 相关阅读:
    交换机基础(四):MSTP负载均衡配置案例
    【斯坦福大学公开课CS224W——图机器学习】四、Link Analysis: PageRank
    Qunar风控安全产品的探索之路
    记录一次远程仓库删除.idea文件,Git提交冲突问题
    vulnhub之Ripper
    java多线程(二)实现多线程的几种方式
    redis的性能管理
    一套仿阿里完整版Spring Boot电商项目,前后端分离+权限管理系统
    ElasticSearch索引基本查询语法
    数据仓库建模设计
  • 原文地址:https://blog.csdn.net/Qstar666/article/details/134483443