动态规划专题Day1——打家劫舍系列

最近很久没有刷算法了感觉脑子被糊住了,因此复习一些基本的算法,路线参考自Claude Ops4.7体感下来确实是很好的路线。

Day1主要是从一维到环形到树形,理解"选 / 不选"两个状态,但是个人感觉这一部分其实是最简单的DP题目,尤其是再定义转移公式的时候完全不用动脑子

题号 题名 难度 备注
70 爬楼梯 简单 热身 5 min,写出空间优化版
198 打家劫舍 中等 Hot 100 复习,这次必须写出滚动数组版
213 打家劫舍 II 中等 环形 → 拆成两次线性
337 打家劫舍 III 中等 树形 DP 入门题,返回二元组 偷, 不偷
LC 70 爬楼梯
  • 题目

出现的问题主要是这里dp0的值到底是多少:

复制代码
class Solution {
    public int climbStairs(int n) {
        // dp[i]表示从0到第i阶有多少种方法
        if(n < 2)return 1;
        int[] dp = new int[n+1];
        dp[0] = 0; // 这里应该是1
        dp[1] = 1;
        for(int i = 2;i <= n;i++){
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];
    }
}

因为DP 里的 dp0 不是在问"动作",而是在问"有多少种方案能让我停在第 0 阶 "。那因此答案是:有 1 种 ------ 就是「啥也不做」这一种

当然其实也可以从 dp2 开始定义初值(绕开 dp0 的哲学问题)

第一题主要出现的问题就是再定义dp0的时候其实是没有想清楚的,dp定义是从0到0没有别的方法因此是1,但是不小心理解为0到0不需要操作因此不存在方法,这个确实仔细想想是有问题的

LC 198 打家劫舍
  • 题目

出现的问题主要是这里dp1的值的计算方式:

复制代码
class Solution {
    public int rob(int[] nums) {
        // dp[i]表示在i号房屋之前(从0开始)可以偷到的最多的钱
        if(nums.length < 2)return nums[0];
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = nums[1]; // 这里应该是dp[1] = Math.max(nums[0],nums[1]);
        for(int i = 2;i < dp.length;i++){
            dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
        }

        return dp[nums.length-1];
    }
}

主要是其实就算是dp1其实也是符合**dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);**的关系式子的,只是因为没有dp-1而单独拎出来的而已。因此主要是初始化的问题

这个题其实更多的是自己粗心导致的问题,再dp1初始化的时候没有想清楚就动笔了,千万要记住dp数组初始化是最关键的两个步骤中的一个一定需要仔细了

LC 213 打家劫舍II →成环
  • 题目

    class Solution {
    public int rob(int[] nums) {
    // 两种情况进行讨论
    int[] dp = new int[nums.length];
    // 先考虑如果第一家开始偷了
    if(nums.length < 2)return nums[0];
    dp[0] = nums[0];
    dp[1] = nums[0];
    for(int i = 2;i < nums.length - 1;i++){
    dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
    }
    int max = dp[nums.length - 2]; // 主要需要注意这里
    //如果第一家不去偷
    dp[0] = 0;
    dp[1] = nums[1];
    for(int i = 2;i < nums.length;i++){
    dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
    }
    max = Math.max(max,dp[nums.length - 1]);
    return max;
    }
    }

主要的错误就是**第一个地方的max应该是dp[nums.length - 2],因为最后一个不允许偷盗。**而下面的第一家不偷,因此最大值才在dp[nums.length - 1]

这个题目分类讨论的想法我是想到了的,但是对于这里int max = dp[nums.length - 2]这一部分的时候定义一定需要注意,因为我第一次就是写错了的

而成环和不成环其实很多题目都是用这种思想来进行处理的->使用分类讨论等方法将其转换成普通的不成环的题目,然后正常求解

LC 337 打家劫舍III →数状
  • 题目

    class Solution {
    public int rob(TreeNode root) {
    int[] x = robSub(root);
    return Math.max(x[0],x[1]);
    }

    复制代码
      private int[] robSub(TreeNode root){
          int[] x = new int[2];
          if(root == null)return x;
          // int[0]表示不偷当前节点
          int[] l = robSub(root.left);
          int[] r = robSub(root.right);
          x[0] = l[1] + r[1]; // 这个地方不应该强制子节点去偷,而是选择里面最大的方案
          // x[0] = Math.max(l[0],l[1]) + Math.max(r[0],r[1]);
          x[1] = l[0] + r[0] + root.val;
          return x;
      }

    }

这个题目最主要的问题就是不应该是x[0] = l[1] + r[1];,而应该x[0] = Math.max(l[0],l[1]) + Math.max(r[0],r[1]); 因为如果是前者就相当于不偷当前节点但是强制偷子节点,显然这个不一定是最大的,因此这里需要一个继续的取舍抉择,有一个最大值

这个题目虽然说是dp问题,而且确实使用了数组进行了记忆,但是其实这个更多意义上是一个标准的树的递归求解思路,这里递归求解的时候一定需要注意这里面的公式呀,因为我就是这么出错的呢,没办法还是太菜了

相关推荐
落羽的落羽14 分钟前
【项目】JsonRpc框架——开发实现1(细节功能、字段定义、抽象层、具象层)
linux·服务器·网络·c++·人工智能·算法·机器学习
handler0135 分钟前
【算法】并查集(普通/扩展/带权)模板与例题
数据结构·c++·笔记·算法·c·图论·查并集
qq7422349841 小时前
从“感知”到“决断”:测评百度伐谋产业决策智能体的端到端推理与行动机制
人工智能·算法·百度·大模型·运筹优化
huohaiyu2 小时前
深入解析Java垃圾回收机制
java·开发语言·算法·gc
汉克老师2 小时前
GESP6级C++考试语法知识(五十三、动态规划----背包问题(六、分组背包)
c++·动态规划·背包问题·gesp6级·gesp六级·分组背
浮芷.2 小时前
鸿蒙PC端 TTS 并发调用问题详解:资源竞争与队列管理
算法·华为·开源·harmonyos·鸿蒙·鸿蒙系统
装不满的克莱因瓶2 小时前
掌握感知器的学习原理
人工智能·python·神经网络·算法·ai·卷积神经网络
Lsk_Smion2 小时前
力扣实训 _ [994].腐烂的橘子/图论
算法·leetcode·图论
轻微的风格艾丝凡2 小时前
两电平三相VSC整流模式从不控整流平滑切换至有源整流调试记录
算法·dsp·c2000
dongf20192 小时前
R语言KNN算法
算法·数据分析·r语言