337. 打家劫舍 III
337. 打家劫舍 III
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root
。
除了 root
之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root
。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
示例 1:
输入: root = [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
示例 2:
输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9
提示:
- 树的节点数在
[1, 104]
范围内 0 <= Node.val <= 104
经典的打家劫舍问题,我觉得更偏向于动态规划,这类问题只要寻找[状态]和[选择]即可,每家要么就抢要么是不抢,同时对于一个点,有时候总会有不同的选择方法走到这个点,如果有备忘录,就可以大大优化效率
class Solution {
public:
//备忘录 记录每次的节点做出的选择
unordered_map<TreeNode* ,int> memo;
int rob(TreeNode* root) {
if(root==nullptr)return 0;
//该节点是否做过选择
if(memo.find(root)!=memo.end()){
return memo.at(root);
}
//抢这一家,抢的下一家就不是相连的
int do_it=root->val+(root->left==nullptr?0:rob(root->left->left)+rob(root->left->right))
+(root->right==nullptr?0:rob(root->right->right)+rob(root->right->left));
//不抢这一家 就可以去相连的选择
int not_do_it=rob(root->left)+rob(root->right);
//取最大值
int res=max(do_it,not_do_it);
//记录下选择过的节点
memo.emplace(root,res);
return res;
}
};
说到打家劫舍,就顺手复习一下这一系列的问题,也是参考的《labuladong的算法小抄》。
经典动态规划:打家劫舍系列问题
House Robber I
假想你就是这个专业强盗,从左到右走过这一排房子,在每间房子前都有两种选择:抢或者不抢。
如果你抢了这间房子,那么你肯定不能抢相邻的下一间房子了,只能从下下间房子开始做选择。
如果你不抢这间房子,那么你可以走到下一间房子前,继续做选择。
当你走过了最后一间房子后,你就没得抢了,能抢到的钱显然是 0(base case)。
以上的逻辑很简单吧,其实已经明确了「状态」和「选择」:你面前房子的索引就是状态,抢和不抢就是选择。
备忘录出现的原因
就可以发现对于同一start
位置,是存在重叠子问题的,比如下图:
盗贼有多种选择可以走到这个位置,如果每次到这都进入递归,岂不是浪费时间?所以说存在重叠子问题,可以用备忘录进行优化
House Robber II
这道题目和第一道描述基本一样,强盗依然不能抢劫相邻的房子,输入依然是一个数组,但是告诉你这些房子不是一排,而是围成了一个圈。
首先,首尾房间不能同时被抢,那么只可能有三种不同情况:要么都不被抢;要么第一间房子被抢最后一间不抢;要么最后一间房子被抢第一间不抢。
那就简单了啊,这三种情况,哪种的结果最大,就是最终答案呗!不过,其实我们不需要比较三种情况,只要比较情况二和情况三就行了,****因为这两种情况对于房子的选择余地比情况一大呀,房子里的钱数都是非负数,所以选择余地大,最优决策结果肯定不会小。
本文来自博客园,作者:{BailanZ},转载请注明原文链接:https://www.cnblogs.com/BailanZ/p/16138998.html