动态规划day09(打家劫舍,树形dp)

这篇具有很好参考价值的文章主要介绍了动态规划day09(打家劫舍,树形dp)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

198.打家劫舍

看到题目的第一想法

看到代码随想录之后的想法

自己实现过程中遇到的困难

213.打家劫舍II

看到题目的第一想法

看到代码随想录之后的想法

自己实现过程中遇到的困难

337.打家劫舍 III(树形dp)

看到题目的第一想法

看到代码随想录之后的想法

自己实现过程中遇到的困难


198.打家劫舍

力扣题目链接(opens new window)

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

  • 示例 1:
  • 输入:[1,2,3,1]
  • 输出:4

解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。   偷窃到的最高金额 = 1 + 3 = 4 。

  • 示例 2:
  • 输入:[2,7,9,3,1]
  • 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。   偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 400
看到题目的第一想法

        dp[j]设置为能偷到的最高金额

        由于相邻的不能偷于是最高金额应该从j-2,j-3种去取

        dp[j] = nums[i]+math.Max(dp[j-2],dp[j-3]);

        最终返回dp[j]与dp[j-1]中的最大值

看到代码随想录之后的想法

        我的想法并没有完全按照dp数组的定义来写,不具备普适性

        代码随想录中,dp的定义为能偷到的最高金额

        则目标元素应该有两种选择

        1 偷目标元素 :若偷目标元素为dp[j-2]+nums[i]

        2 不偷目标元素:若不偷目标元素为dp[j-1]

       dp[j] = Math.max(dp[j-2]+nums[i],dp[j-1]);

自己实现过程中遇到的困难

        初始化时 dp[0] = nums[0]

                        dp[1] = Math.max(nums[0],nums[1]); 应该是取两者的最大值,才能保证偷到最多

                        我写成了dp[1] = nums[1]

class Solution {
    /*
    public int rob(int[] nums) {
        // 定义dp数组以及每个下标的含义
        // 到达dp[i] 能偷窃到的最高金额
        // 确定递归函数
        // dp[i] = nums[i]+Math.max(dp[i-2],dp[i-3]);
        // 确定遍历顺序
        // 从前往后
        // dp数组初始化
        // dp[0] = nums[0], dp[1] = nums[1] , dp[2] = nums[2]+nums[0] 
        // dp3 = nums[i]+Math.max(dp[i-2],dp[i-3])
        // 举例推导dp数组
        int[] dp = new int[nums.length];
        if(nums.length==1){
            return nums[0];
        }
        if(nums.length==2){
            return Math.max(nums[0],nums[1]);
        }
        if(nums.length==3){
            return Math.max(nums[0]+nums[2],nums[1]);
        }
        dp[0] = nums[0];
        dp[1] = nums[1];
        dp[2] = nums[2] + nums[0];
        for(int i=3;i<nums.length;i++){
            dp[i] = nums[i]+Math.max(dp[i-2],dp[i-3]);
        }
        return Math.max(dp[nums.length-2],dp[nums.length-1]);
    }*/
    //卡哥思路
    public int rob(int[] nums) {
        //我的方法没有遵循dp数组的定义,dp[i] 代表当前最高金额
        // 当前最高金额,如果偷当前nums[i] 则dp[i] = nums[i]+dp[i-2]
        // 如果不偷当前nums[i] 则为dp[i-1]
        // 定义dp数组以及每个下标的含义
        // 考虑下标i 能偷到的最高金额
        // 确定递归函数
        // 对于nums[i] 我们能考虑是偷还是不偷
        // 若偷i 则 dp[i] = nums[i]+dp[i-2]
        // 若不偷i 则 dp[i] = dp[i-1]
        // dp[i] = nums[i]+Math.max(dp[i-2],dp[i-3]);
        // 确定遍历顺序
        // 从前往后
        // dp数组初始化
        // dp[0] = nums[0], dp[1] = nums[1] , dp[2] = nums[2]+nums[0] 
        // dp3 = nums[i]+Math.max(dp[i-2],dp[i-3])
        // 举例推导dp数组
        int[] dp = new int[nums.length];
        if(nums.length==1){
            return nums[0];
        }
        if(nums.length==2){
            return Math.max(nums[0],nums[1]);
        }
        dp[0] = nums[0];

        //这里应该是Math.max(nums[0],nums[1]);我之前直接写成nums[1] 是错误的
        dp[1] = Math.max(nums[0],nums[1]);
        for(int i=2;i<nums.length;i++){
            dp[i] = Math.max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[nums.length-1];
    }
}

213.打家劫舍II

力扣题目链接(opens new window)

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。

示例 1:

  • 输入:nums = [2,3,2]

  • 输出:3

  • 解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

  • 示例 2:

  • 输入:nums = [1,2,3,1]

  • 输出:4

  • 解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。

  • 示例 3:

  • 输入:nums = [0]

  • 输出:0

看到题目的第一想法

        和打家劫舍1差不多,只是要考虑首尾

        先处理[0,nums[length-2]]

        再处理[1,nums[length-1]]

        返回两者的最大值

看到代码随想录之后的想法

        和我思路差不多

自己实现过程中遇到的困难

        初始化时 dp[0] = nums[0]

                        dp[1] = Math.max(nums[0],nums[1]); 应该是取两者的最大值,才能保证偷到最多

                        我写成了dp[1] = nums[1]

class Solution {
    public int rob(int[] nums) {
        // 确定dp以及每个下标的含义
        // 第一个房屋和最后一个是紧挨着的
        // 主要是没办法确定是否加上了第一个
        // 今晚能偷窃到的最高金额
        // 我的思路 ,两次dp 一次从第一个到倒数第二个
        // 一次从第二个到最后一个。
        // 确定递推函数
        // dp[i] = Math.max(nums[i]+dp[i-2],dp[i-1]);
        // 确定遍历顺序
        // 从前往后
        // dp数组初始化
        // dp[0] = nums[0],dp[1]=Math.max(nums[0],nums[1]);
        // 举例推导dp数组
        int[] dp1 = new int[nums.length-1];
        int[] dp2 = new int[nums.length-1];
        if(nums.length==1){
            return nums[0];
        }
        if(nums.length==2){
            return Math.max(nums[0],nums[1]);
        }
        if(nums.length==3){
            return Math.max(Math.max(nums[0],nums[1]),nums[2]);
        }
        dp1[0] = nums[0];
        dp1[1] = Math.max(nums[0],nums[1]); 
        dp2[0] = nums[1];
        dp2[1] = Math.max(nums[1],nums[2]);
        for(int i=2;i<nums.length-1;i++){
            dp1[i] = Math.max(dp1[i-1],nums[i]+dp1[i-2]);
            dp2[i] = Math.max(dp2[i-1],nums[i+1]+dp2[i-2]);
        }
        return Math.max(dp1[nums.length-2],dp2[nums.length-2]);

    }
}

337.打家劫舍 III(树形dp)

力扣题目链接(opens new window)

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

动态规划day09(打家劫舍,树形dp),动态规划,算法

看到题目的第一想法

        应该使用什么递归思路?前序?后续?

        dp数组要怎么定义?无思路

看到代码随想录之后的想法

        卡哥更多的是考虑二叉树的遍历

        每个节点两种状态,偷该节点,不偷该节点

        要获取偷该节点的最大值,和不偷该节点的最大值

        使用后序遍历,需要通过子节点偷还是不偷,来确定父节点是否要偷

        每层递归通过一个数组dp[2] 来告诉上一层,偷与不偷的值

         new int{偷该层的最大值,不偷该层的最大值}

        父节点通过左右孩子的返回值来确定偷or不偷的最大值,再往上返回

        若该节点要偷:左右孩子不偷的值相加,同时与自己的值相加

        dp[0] = left[0]+right[0]+val

        若该节点不偷:左右孩子偷与不偷的最大值                 

        dp[1] = Math.max(left[0],left[1])+Math.max(right[0],right[1])

        递归三要素

        1 确定递归函数的参数和返回值

             参数为root,返回值为int[]

        2 确定递归函数的终止条件

                若root==null 则return new int[]{0,0}

        3 确定递归函数的执行逻辑

                后序遍历

                先递归执行root.left

                再递归执行root.right

                通过left和right返回的int数组,来确定自身的int[]并返回

        确定dp数组以及下标的含义

        确定递推函数

        dp数组初始化

        确定遍历顺序

        举例推导dp数组

自己实现过程中遇到的困难

        树形dp没见过,考虑在每个递归中新增一个dp[] 来确保内容的传递

        确定每个元素的动作比较关键,比如这个题目就是要你看该层是偷or不偷?

        然后分别讨论

        文章来源地址https://www.toymoban.com/news/detail-817995.html

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        //卡哥思想
        // 确定递归的参数和返回值
        // 参数 root ,返回值 dp[2] 
        // 确定递归的终止条件
        // 如果为null return {0,0}
        // 确定递归函数的执行逻辑
        // 看是否偷当前节点,分为偷or不偷
        // 这道题是树形dp的基础题目
        // 使用一个数组,记录两个状态{偷当前的最高金额,不偷当前的最高金额}
        // 通过父节点看子节点中的金额
        // 每个节点有两个状态:
        //   偷:子节点不偷,将两个子节点不偷的值(left[1],right[1])+自己节点的值
        // 不偷:子节点偷or不偷的最大值相加,max(left[0],left[1])+max(right[0],right[1])
        //确定dp数组和下标的含义
        // dp[0] 偷的最高金额
        // dp[1] 不偷的最高金额
        //用数组来记录小偷能偷取的最大值
        //确定递推公式
        // dp[0] = left[1]+right[1]+val;
        // dp[1] = Math.max(left[0],left[1])+max(right[0],right[1])
        //DP数组初始化
        //都为0
        //确定遍历顺序
        //后续遍历
        //举例推导dp数组
        int[] dp = getMax(root);
        return Math.max(dp[0],dp[1]);

    }
    int[] getMax(TreeNode root){
        if(root==null){
            return new int[2];
        }
        //后续遍历,左右中
        int[] left = getMax(root.left);
        int[] right = getMax(root.right);
        int[] dp = new int[2];
        //看是否偷当前的元素
        //偷当前的元素,则左右不偷
        dp[0] = left[1]+right[1] + root.val;
        //不偷当前的元素
        dp[1] = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
        return dp;
    }
}

到了这里,关于动态规划day09(打家劫舍,树形dp)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态规划_打家劫舍(Ⅰ~Ⅲ)

    打家劫舍系列 返回最大金额 不能同时取相邻两个数 数组数据全部非负 ①dp数组含义 dp[i]表示前i个数中按规则取出的最大总和 ②递推公式 dp[i]=max(dp[i-1],dp[i-2]+nums[i]) 当前最优可以从两个状态推出(前提是前面已经为最优解): 1° 前一个数未取:则当前数取了,则总和最大

    2024年02月03日
    浏览(40)
  • 【算法|动态规划No.10】leetcode LCR 089. 打家劫舍 & LCR 090. 打家劫舍 II

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望对大家有所帮助 🍓希望我们一起努力、成长,共同进步。

    2024年01月20日
    浏览(44)
  • 力扣198. 打家劫舍(java 动态规划)

    Problem: 198. 打家劫舍 1.构建多阶段决策模型:n个房屋对应n个阶段,每一个阶段决定一个房间是偷还是不偷,两种决策:偷、不偷 2.定义状态:不能记录每个阶段决策完之后,小偷可偷的最大金额,需要记录不同决策对应的最大金额,也就是:这个房屋偷-对应的最大金额;这

    2024年01月21日
    浏览(54)
  • 【学会动态规划】打家劫舍 II(12)

    目录 动态规划怎么学? 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后: 学习一个算法没有捷径,更何况是学习动态规划, 跟我一起刷动态规划算法题,一起学会动态规划! 题目链接:213. 打家劫舍 II - 力扣(Lee

    2024年02月15日
    浏览(40)
  • leetcode-打家劫舍专题系列(动态规划)

    你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动

    2024年04月14日
    浏览(45)
  • 【LeetCode热题100】198. 打家劫舍(动态规划)

    你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统, 如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动

    2024年04月11日
    浏览(46)
  • 动态规划-经典dp(打家劫舍,股票等)

    1.1.1 爬楼梯  由于求的是组合数,我们将不同路径相加即可 dp定义: dp[i]为爬到第i阶楼梯的方法数; 转移方程: 初始化:  由于涉及到i-2和i-1,那么我们要从i=2开始遍历,因此要初始化dp[0] = 0,dp[1] = 1(根据定义) 遍历顺序: 从左往右  完整代码:  1.1.2 使用最小花费爬楼梯

    2024年01月19日
    浏览(37)
  • leetcode 343.整数拆分 198.打家劫舍(动态规划)

       OJ链接 :leetcode 343.整数拆分 代码:  OJ链接 :198.打家劫舍    代码:

    2024年02月05日
    浏览(49)
  • Java 动态规划 Leetcode 213. 打家劫舍 II

    代码展示:         该题其实是Java 动态规划 面试题 17.16. 按摩师的变种,增加了一个首尾是相邻的条件,而我们解决该题也要用到链接的这道题的思想,可以先去看一下上面这篇博客 此题可以采用动态规划的方法进行解决,根据解决动态规划题目的5大步骤进行逐步分析

    2024年02月13日
    浏览(40)
  • C++动态规划经典试题解析之打家劫舍系列

    力扣上有几道与打家劫舍相关的题目,算是学习动态规划时常被提及的经典试题,很有代表性,常在因内大大小小的社区内看到众人对此类问题的讨论。 学习最好的方式便是归纳总结、借鉴消化,基于这个目的,本文对此类问题也做了讲解,在一些优秀思想的基础上添加了个

    2024年02月13日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包