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

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

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

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

出现的问题主要是这里dp[0]的值到底是多少:

复制代码
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 里的 dp[0] 不是在问"动作",而是在问"有多少种方案能让我停在第 0 阶 "。那因此答案是:有 1 种 ------ 就是「啥也不做」这一种

当然其实也可以从 dp[2] 开始定义初值(绕开 dp[0] 的哲学问题)

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

LC 198 打家劫舍
  • 题目

出现的问题主要是这里dp[1]的值的计算方式:

复制代码
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];
    }
}

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

这个题其实更多的是自己粗心导致的问题,再dp[1]初始化的时候没有想清楚就动笔了,千万要记住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问题,而且确实使用了数组进行了记忆,但是其实这个更多意义上是一个标准的树的递归求解思路,这里递归求解的时候一定需要注意这里面的公式呀,因为我就是这么出错的呢,没办法还是太菜了

相关推荐
khalil10201 小时前
代码随想录算法训练营Day-52 图论03 | 101.孤岛的总面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿
c++·算法·图论
老四啊laosi1 小时前
[滑动窗口] 13. 水果成篮
算法·leetcode·滑动窗口·水果成篮
刀法如飞1 小时前
Palantir技术原理深度分析:Ontology 存储结构与读写方式
人工智能·算法·架构
澈2071 小时前
图论基础:邻接矩阵与邻接表详解
算法·图论·邻接矩阵
白日做梦Q1 小时前
Miniconda 新手保姆级教程:从安装到熟练使用(全程无跳步,避坑指南附全)
人工智能·深度学习·算法·机器学习
吃好睡好便好1 小时前
在Matlab中绘制变半径柱面图
开发语言·人工智能·学习·算法·matlab
拂拉氏2 小时前
【知识讲解-题目讲解】:二叉树的前、中、后序遍历的三种实现(递归,非递归,Morris遍历)与二叉树oj题讲解(二叉树最近公共祖先,二叉树展开为链表)
数据结构·算法·leetcode·二叉树·遍历
luck_bor2 小时前
File 类核心笔记
java·前端·算法
anew___2 小时前
从高方差到稳定训练:深度强化学习算法演进全解析
算法