今天总结三道题,有关打家劫舍问题。
- 打家劫舍
示例 1: 输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
如何抽象为动态规划问题?dp[i]是在第i家时,能偷窃到的最高金额。自己想出来的,值得鼓励。递推公式为dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]) 状态由偷当前节点,和不偷当前节点推出来的。接下来的问题就是初始化,和验证的问题。
- 打家劫舍II
示例 1:
输入:nums = [2,3,2] 输出:3 解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。 环形房屋。
环形房屋。分情况讨论:1.考虑第一个节点 2. 考虑最后一个节点。 封装基本的函数,然后分区间对两种情况求解,然后取最大的值。
- 打家劫舍III
树型结构,一开始的思路是层序遍历,然后隔行取,有问题。因为也有可能搁两行甚至更多,偷得的钱更多。 难点在于如何将递推公式应用到树形结构。结合对树形遍历的方法,选择前序遍历还是后序遍历也是一个需要思考的问题。这里选择后序遍历,可以这么理解,从判断底部的左右节点偷或不偷,来推出当前节点的状态,并返回,不断推到根节点。节点的状态可以由偷/不偷来不断递推。如何维护这两个状态值?这里的dp[i] = vector {0,0} 为什么这里是两个?之前问题中都是通过dp[i] = int 来维护的。可以这样来理解,之前的问题中,对过去的状态可以由dp[i] = int 来描述,但是在这道题中,无法通过单个值来进行表述当前的状态,因此考虑增加维度来实现状态的传递。
```
vector<int> robTree(TreeNode* cur) {
if (cur == NULL) return vector<int>{0, 0};
vector<int> left = robTree(cur->left);
vector<int> right = robTree(cur->right);
// 偷cur,那么就不能偷左右节点。
int val1 = cur->val + left[1] + right[1];
// 不偷cur,那么可以偷也可以不偷左右节点,则取较大的情况
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
return {val2, val1};
}
```