动态规划专题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问题,而且确实使用了数组进行了记忆,但是其实这个更多意义上是一个标准的树的递归求解思路,这里递归求解的时候一定需要注意这里面的公式呀,因为我就是这么出错的呢,没办法还是太菜了

相关推荐
顶点多余2 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
罗西的思考4 小时前
【Agentic RL / 强化学习框架】Molt 设计解读
人工智能·算法·机器学习
hahaha60164 小时前
HLS高层次综合设计技巧--C++类和模板
图像处理·人工智能·算法·计算机视觉
多弗朗皮卡丘5 小时前
算法详解4:买卖股票的最佳时机系列(上)
算法
维克兜率天7 小时前
【维克】动量指标家族:RSI、ROC、CCI、Momentum全面解析
python·算法
AI情绪识别开源8 小时前
检信 AI 智能推广平台(代号:JX-Promote)
人工智能·算法·erlang
老当益壮梁奶奶9 小时前
Linux软件编程学习笔记(八):进程间通信详解(1)
linux·c语言·笔记·学习·算法
不会就选b9 小时前
算法日常・每日刷题--<BFS拓扑排序>4
算法
不正经学生9 小时前
C语言动态内存管理(上):堆上的自由与责任
c语言·开发语言·c++·算法·面试
伟大的车尔尼9 小时前
贪心的概念
数据结构·算法·贪心