算法训练营day41 | {198.打家劫舍, 213.打家劫舍II, 337.打家劫舍III}
第九章动态规划part07,今天是打家劫舍的一天。
198. 打家劫舍
- 题目链接:198.打家劫舍
- 文档讲解:代码随想录
- 视频讲解:动态规划,偷不偷这个房间呢?| LeetCode:198.打家劫舍
- 状态:当前的状态到底是偷还是不偷,自由度确实很高,一开始不知道怎么dp。
1.1 解题分析
当前房屋偷与不偷取决于前一个房屋和前两个房屋是否被偷了,所以感觉到当前状态和前面状态会有一种依赖关系,这种依赖关系就是动规的递推公式!
将大致思路进一步转化成动规五部曲分析:
- dp数组定义:dp[i]表示 考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。
- 递推公式:
- 如果偷当前房屋 dp[i] = dp[i - 2] + num[i]
- 如果不偷当前房屋 dp[i] = dp[i - 1]
- dp[i] = MAX(dp[i - 2] + num[i], dp[i - 1])
- dp数组初始化:
- 依赖于前两个状态,所以初始化dp[0] = num[0]; dp[1] = MAX(num[0], num[1])
- 遍历顺序:从前往后遍历即可
- 打印dp数组。
1.2 解题小结
▶
198. 打家劫舍1 | |
213. 打家劫舍II
- 题目链接:213.打家劫舍II
- 文档讲解:代码随想录
- 视频讲解:动态规划,房间连成环了那还偷不偷呢?| LeetCode:213.打家劫舍II
- 状态:与上一题的区别是房间成环了,怎么解决环的问题
2.1 解题分析
环形问题不好确定起始位置在哪里,终止位置在哪里,把环形展开成一个线形的一个结构,再单独去考虑首元素和尾元素选或不选,由此分析出三种情况:
- 情况一:考虑不包含首尾元素
- 情况二:考虑包含首元素,不包含尾元素
- 情况三:考虑包含尾元素,不包含首元素
注意这里的用词都是“考虑”,例如情况三中,考虑包含尾元素,但不是一定要偷尾部元素。情况二、三已经包含情况一了,分析到这里,本题剩下的部分就和198. 打家劫舍一样了。
2.2 解题小结
▶
213. 打家劫舍II1 | |
337. 打家劫舍III
- 题目链接:337.打家劫舍III
- 文档讲解:代码随想录
- 视频讲解:动态规划,房间连成树了,偷不偷呢?| LeetCode:337.打家劫舍3
- 状态:树形dp的基础题目,需要熟悉二叉树的遍历。
3.1 解题分析
与前两个打家劫舍如出一辙,只不过这个换成了树,关键是要讨论当前节点抢还是不抢。
如果抢了当前节点,两个孩子就不能动,如果没抢当前节点,就可以考虑抢左右孩子。
3.2 解题小结
3.2.1 暴力递归+记忆化递推
因为递归涉及到重复计算,使用一个map把计算过的结果保存一下,这样如果计算过孙子,那么计算孩子的时候可以复用孙子节点的结果。这种解法属于自顶向下的树形DP。
- 时间复杂度:O(n)
- 空间复杂度:O(log n)
▶
337. 打家劫舍III1 | |
3.2.2 动态规划
动态规划使用状态转移容器来记录状态的变化,用一个长度为2的数组,记录当前节点偷与不偷所得到的最大金钱。不用向暴力递归一样,对一个节点偷与不偷得到的最大金钱没有记录,需要实时计算。
递归三部曲:
- 返回值和形参:返回值是一个长度为2的数组,保存一个节点偷与不偷两个状态所得到的金钱。参数为当前节点。
- 递归的终止条件:当遍历到空节点时,返回 {0,0}
- 单层递归逻辑:分别计算偷当前节点能得到的最大金额,不偷当前节点能得到的最大值,以数组的形式返回。
▶
337. 打家劫舍III1 | |
第一遍看视频,没想到的是不偷父节点的时候,左右孩子偷不偷仍然是考虑,隔着位置偷不一定是最好的。
4. 今日收获
- 打家劫舍,从线性数组、成环到一个二叉树形,关键是想清楚某一个节点偷与不偷,依赖于哪几个节点的状态,从而找到递推关系,使用dp解题。
- 学习时长:3小时
算法训练营day41 | {198.打家劫舍, 213.打家劫舍II, 337.打家劫舍III}
http://paopaotangzu.xyz/cn/day41_leetcode/