当前位置: 首页 > news >正文

Day39--动态规划--198. 打家劫舍,213. 打家劫舍 II,337. 打家劫舍 III

Day39–动态规划–198. 打家劫舍,213. 打家劫舍 II,337. 打家劫舍 III

今天的重点在于最后一题:树形动态规划,树形DP。耳目一新,但是总的思路是一样的。

198. 打家劫舍

思路:

  1. 确定dp[i]含义:考虑下标[0,i]以内的房屋,最多可以偷窃的金额为dp[i]。(不触发警报)
  2. 递推公式:
    • 对于每个房屋,有两种选择:抢劫或不抢劫
    • 抢劫:则加上当前房屋金额和前前个房屋的最大金额
    • 不抢劫:则取前一个房屋的最大金额
    • 所以递推公式:dp[i] = Math.max(dp[i-2]+nums[i],dp[i-1]);
  3. 初始化:dp[0] = nums[0],dp[1] = Math.max(nums[0],nums[1]);(这里要注意没有nums[1]的情况)
  4. 遍历顺序:从前往后
class Solution {public int rob(int[] nums) {int n = nums.length;// 初始化前注意处理nums[1];if (n == 1) {return nums[0];}int[] dp = new int[n];// 初始化dp[0] = nums[0];dp[1] = Math.max(nums[0], nums[1]);// 动态规划for (int i = 2; i < n; i++) {dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);}return dp[n - 1];}
}

思路:

观察得知,dp[i] 只跟 dp[i-2] 和 dp[i-1]有关,所以可以节省空间,dp数组不用开n个空间,开三个空间就够了。

// 滚动数组
class Solution {public int rob(int[] nums) {int n = nums.length;// 初始化前注意处理nums[1];if (n == 1) {return nums[0];} else if (n == 2) {return Math.max(nums[0], nums[1]);}// 需要三个变量int[] dp = new int[3];// 初始化dp[0] = nums[0];dp[1] = Math.max(nums[0], nums[1]);// 动态规划for (int i = 2; i < n; i++) {dp[2] = Math.max(dp[0] + nums[i], dp[1]);// 数组往前滚动复制(就像斐波那契数一样)dp[0] = dp[1];dp[1] = dp[2];}return dp[2];}
}

踩坑:注意这里,要特别注意n == 2的情况,是不进循环的,dp[2]没有被赋值,直接返回dp[2]是错误的。要单独处理这个情况。

213. 打家劫舍 II

思路:

情况一:不考虑抢劫最后一个房屋,从0到n-1

情况二:不考虑抢劫第一个房屋,从1到n

取两者之间较大值。其余步骤同上题。

class Solution {public int rob(int[] nums) {int n = nums.length;if (n == 0) {return 0;}if (n == 1) {return nums[0];}// 情况一:不考虑抢劫最后一个房屋,从0到n-1int res1 = robRange(nums, 0, n - 1);// 情况二:不考虑抢劫第一个房屋,从1到nint res2 = robRange(nums, 1, n);return Math.max(res1, res2);}// 198.打家劫舍的核心逻辑// 注意这里的 start 和 end 是左闭右开private int robRange(int[] nums, int start, int end) {int n = end - start;if (n == 1) {return nums[start];} else if (n == 2) {// 使用滚动dp数组的时候,记得要处理n==2的情况,因为会不进循环,返回错误结果return Math.max(nums[start], nums[start + 1]);}// 滚动dp数组,只存储三个状态int[] dp = new int[3];// 初始化dp[0] = nums[start];dp[1] = Math.max(nums[start], nums[start + 1]);// 从start+2开始计算每个位置的最大抢劫金额for (int i = start + 2; i < end; i++) {dp[2] = Math.max(dp[0] + nums[i], dp[1]);// 数组往前滚动复制(就像斐波那契数一样)dp[0] = dp[1];dp[1] = dp[2];}return dp[2];}
}

337. 打家劫舍 III

方法:递归法

思路:

递归法的关键,是要想到用一个map缓存已经计算过的结点。不然会超时。

// 后序遍历(递归法)
class Solution {Map<TreeNode, Integer> map = new HashMap<>();public int rob(TreeNode root) {return postorderTravel(root);}private int postorderTravel(TreeNode node) {if (node == null) {return 0;}// 叶子节点直接返回其值if (node.left == null && node.right == null) {return node.val;}// 如果已经计算过,直接返回缓存结果if (map.containsKey(node)) {return map.get(node);}// 方案一:偷当前节点int val1 = node.val;// 如果左孩子存在,加上左孩子的左右孩子if (node.left != null) {val1 += rob(node.left.left) + rob(node.left.right);}// 如果右孩子存在,加上右孩子的左右孩子if (node.right != null) {val1 += rob(node.right.left) + rob(node.right.right);}// 方案二:不偷当前节点,考虑左右孩子int val2 = rob(node.left) + rob(node.right);// 取两种方案的最大值,并缓存结果int res = Math.max(val1, val2);map.put(node, res);return res;}
}

方法:动态规划

思路:

树形DP。树形贪心。

这里dp只需要记录两个状态,相当于原来的dp[n-1]和dp[n-2],每个dp[i]都是在本层计算,然后刷新返回给上一层的dp[]。

步骤:

  1. dp数组的含义:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。
  2. 递归公式:
    1. 如果是偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0];
    2. 如果不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷一定是选一个最大的,所以:val2 = max(left[0], left[1]) + max(right[0], right[1]);
  3. 确定终止条件:if (cur == NULL) return vector<int>{0, 0};
  4. 遍历顺序:后续遍历
// 动态规划
class Solution {public int rob(TreeNode root) {int[] res = postorderTravel(root);return Math.max(res[0], res[1]);}// 长度为2的数组。dp[0]存放,不偷本节点的情况的最大值,dp[1]存放偷本节点的最大值private int[] postorderTravel(TreeNode node) {if (node == null) {return new int[] { 0, 0 };}// 左右中// 左int[] left = postorderTravel(node.left);// 右int[] right = postorderTravel(node.right);// 中// 方案一:偷当前节点,那么就不能偷左右节点。int val1 = node.val + left[0] + right[0];// 方案二:不偷当前节点,那么考虑左右节点偷与不偷的情况,反正取较大的情况// 注意这里val2是要把左右的情况汇总加起来的int val2 = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);// val2是不偷,放在第一个位置;val1是偷,放在第二个位置。return new int[] { val2, val1 };}
}
http://www.dtcms.com/a/324216.html

相关文章:

  • Code Exercising Day 10 of “Code Ideas Record“:StackQueue part02
  • MVCC和日志
  • 国内外主流大模型深度体验与横向评测:技术、场景与未来展望
  • 后置定语:for + 宾语 + 被动不定式
  • CentOS 10在文本控制台模式下修改字体大小
  • 2020/12 JLPT听力原文 问题一
  • LLM多模态模型应用探索调研
  • 【0基础3ds Max】主工具栏介绍(下)
  • 故障诊断 | VMD-CNN-LSTM西储大学轴承故障诊断附MATLAB代码
  • 智慧社区--4
  • 【C++详解】红黑树规则讲解与模拟实现(内附红黑树插入操作思维导图)
  • 本地代码上传Github步骤
  • 《设计模式》UML类图
  • 通过trae开发你的第一个Chrome扩展插件
  • A4.0:继C5.2的BJT理论引申的开关作用的应用示例
  • DAY36打卡
  • 计算机网络:求地址块128.14.35.7/20中的相关信息
  • 枚举-dfs深度优先搜索
  • 女子试穿4条裤子留下血渍赔50元引争议:消费责任边界在哪?
  • C/C++类型转换(C++四大强制类型转换)
  • 北京JAVA基础面试30天打卡06
  • 编程基础之多维数组——矩阵交换行
  • 每日五个pyecharts可视化图表-line:从入门到精通 (2)
  • 周学会Matplotlib3 Python 数据可视化-绘制折线图(Lines)
  • GPT-5与中国AI发展(DeepSeek R1视角)
  • 基于Django的图书馆管理系统的设计与实现
  • drippingblues靶机通关练习笔记
  • Jotai:React轻量级状态管理新选择
  • 【Bluetooth】【Transport层篇】第六章 基于SDIO的蓝牙硬件发送协议 SDIO Transport详解
  • QT常用控件三