动态规划

动态规划:

  • 特点:核心是状态转移【由许多的重叠子问题组成】,

    • 前状态依赖于上一个状态【递推公式】,
    • 其次是初始化【对初始情况进行讨论】
    • 遍历顺序:状态的推到方向判断【回文字串】
  • 经典题型:

    • 基础题型:

      • 斐波那契数:递推公式的推导;
      • 爬楼的方法:多个状态相互叠加得到下一个状态;
      • 爬楼梯的消耗:多个状态相互直接的min,得到下个状态;
      • 不同路径:推导公式在题目中,关键点在初始化;
      • 不同路径II:有障碍,增加路径判断;
      • 整数拆分:贪心更简单,动态规划:拆分为【两个数之和,至少三个数之和】取最大值;
      • 不同的二叉搜索树:递推公式的推导,左子树,右子树;
    • 背包问题:

      • 理论:

        • 背包容量:一定;
        • 物品
          • 体积:物品体积一定;
          • 价值:物品价值一定;
          • 数量:
            • 只有一个:01背包;
            • 无数个:完全背包;
            • 不同物品,数量不同:多重背包;
        • 整体上,分别是背包遍历和物品遍历,一个二维的动态规划;
        • 递推公式:
        • 初始化:是对物品dp[0][i]进行初始化;
        • 遍历顺序:先遍历物品还是先遍历背包都可,先遍历物品更好理解;
        • 组合与排列取决于编列的顺序:
          • 如果求组合数就是外层for循环遍历物品,内层for遍历背包。【之前状态是,左上角】
          • 如果求排列数就是外层for遍历背包,内层for循环遍历物品。【还是有几种方法】
      • 01背包:之前的状态【使用的是左上角的值

        • 分割子集,最后一块石头的重量:这两题都是填充背包,递推公式是求max最有价值的值;
        • 目标和:是一定能分割,求分割的方法有多少种,递推公式是相加,还有初始化考察点,当物品体积为0的时;
        • 1和0:是一个三维的0-1背包问题;
      • 完全背包:之前状态【以当前点为角的左上角【不包含当前点】

      • 多重背包:

        • 相当于对物品在进行迭代一次【单次取一个,两个,三个...】,转化为01背包问题;
      • 总结:

        • 背包递推公式:

          • 是否能装满,最多能装多少,最大价值,最小个数:

            \[dp[j] = (min||max) (dp[j], dp[j - num[i]] + num[i]) \]

          • 问装满背包有多少种方法:

            \[dp[j] = dp[j] + dp[j - num[i]] \]

          • 遍历顺序:【物品 背包】

            • 01背包,完全背包:通常是先物品,在背包【组合】;
            • 完全背包:必须先背包,在物品【排列】;
          • 细节对于一维dp动态数组:

            • 01之前状态:是由当前点的左上矩形状态推导【不包含该点】;【对于物体,得从后向前遍历】;
            • 完全背包:是由当前点的左上矩形状态推导【包含该点】;【直接前向遍历】
    • 打家劫舍:都是基于数组,循环链表,树,等数据结构,递推公式比较单一,考察在相关状态数,其次在于初始化;

    • 股票问题:相当于一个状态下多个节点的相互关系,交织出当前状态,通常分为持节股票节点和不迟有节点【可以交易的最大笔数】,其次初始化和之前状态数相关;

      • 从买卖一次到买卖多次【贪心,记录已有的最小值,相互关系】;
      • 从最多买卖两次到最多买卖k次【同一个状态下的不同节点】,初始化
      • 从冷冻期再到手续费【之前状态的关系【递推】】,初始化
    • 子序列问题:

      • 子序列【不连续】

        • 最长上升【下降】子序列:是对元素的删除【双层迭代,可以回溯,超时】,dp[i]以num[i]为结尾的子序列长度;‘

        • 最长公共子序列:

          if (nums[i] == num[j]) {
              dp[i][j] = dp[i - 1][j - 1]
          } else {
              //相对顺序:正上和正左
          	dp[i][j] = max(dp[i - 1][j - 1],dp[i][j - 1])
          }
          
        • 不相交的线:同最长公共子序列【两个数组的相对顺序不变下的公共子序列】

      • 子序列【连续】

        • 最长连续上升【下降】序列:单层迭代【可以贪心】;

        • 最长公共子数组:【二维动态数组,dp】,以dp[i][j]的是num1[i]和num2[j]的最长子数组;

          if (nums[i] == num[j]) {
              dp[i][j] = dp[i - 1][j - 1]
          }
          //有序,是对角线有序,
          //相对顺序:正上和正左
          dp[i][j] = max(dp[i - 1][j - 1],dp[i][j - 1])
          
        • 最大子数组和:可以贪心

      • 距离编辑

        • 判断子序列:两个子序列的并集【相对顺序:正上和正左】;

          判断字符串的的包含:KMP算法;

          动态规划:拥有初始化,动态数组,并没有双指针性能好;

        • 子序列包含的个数:之前是判断能不能,现在是有多少种方案;

        • 两个字符串的删除操作:之前是只允许删除一个字符串;

        • 距离编辑:

          • 对一个字符串的删除,插入,替换;
            • 删除:dp[i][j - 1];
            • 插入:相当于另一个字符串的删除,dp[i - 1][j];
            • 替换:相当于两个字符串都删除:dp[i - 1][j - 1];

          关键在于dp的定义,用于简化初始化

          常见操作:删除,插入,替换

          类似于空间换效率,相比回溯方法,将所有可能计算出来,【当前状态依赖于左上角的状态】

      • 回文字串题型:

        • 回文子串的数量:考察遍历顺序
        • 最长回文子序列:在字串的基础上,无需就是正上,正左取最大值;

        对于字符串的动态规划:一般分为相不相等,两种情况;

        五部曲:

        1. 确定动态数组dp的含义;
        2. 确定递推公式【有时候,一个状态下有多个节点,股票】
        3. dp的初始化【序列,dp的扩大定义,能更好的初始化】,
        4. 确定遍历顺序【背包问题,顺序关系组合和排序,回文,顺序关系递推公式】
        5. 举例:当前状态和哪些之前状态相关【正上,正左,斜对角,等等】;
posted @ 2025-08-03 15:38  烟雨断桥  阅读(11)  评论(0)    收藏  举报