题目
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。 计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
来自 leetcode-cn.com/problems/ho…
思路+代码
求最值,穷举,bp问题。先明确状态转移方程,bp函数肯定就是求当前这个节点能偷到的最大的钱
对于每一个节点有两种情况:1、偷这个节点,然后偷它的孙子节点 2、不偷这个节点,然后偷它的子节点。
base case就是null
然后返回值就是这个节点能偷到的最多的钱,就是以上两个求max,然后记得用备忘录优化。
class Solution {
// 动态规划问题
public int rob(TreeNode root) {
// 使用备忘录储存用过的值,进行优化
HashMap<TreeNode,Integer> memo = new HashMap<>();
return tou(root,memo);
}
public int tou(TreeNode root,HashMap<TreeNode,Integer> memo){
// base case
if (root == null){
return 0;
}
if(memo.containsKey(root)){
return memo.get(root);
}
// 状态转移方程
int res = root.val;
if(root.left != null){
res += tou(root.left.left,memo)+tou(root.left.right,memo);
}
if(root.right != null){
res += tou(root.right.left,memo)+tou(root.right.right,memo);
}
int result = Math.max(res,tou(root.right,memo)+tou(root.left,memo));
// 放入备忘录
memo.put(root,result);
return result;
}
}
优化压缩
因为其实每个节点都只有偷和不偷两个状态,所以只需要储存至两个状态就可以。0表示不偷,1表示偷。则:
任何一个节点能偷到的最大钱的状态可以定义为
当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱 当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数
直接写出下面这个代码,有个问题:
class Solution {
public int rob(TreeNode root) {
int [] result = bp(root);
return Math.max(result[0],result[1]);
}
public int[] bp(TreeNode root){
if(root == null){
return new int[2];
}
int[] res =new int[2];
int[] left = bp(root.left);
int[] right = bp(root.right);
res[0] = left[1]+right[1];
res[1] = root.val + left[0] +right[0];
return res;
}
}
这个代码有个问题:
就是这个节点不偷的时候,它的左右节点也可以不偷(跨两层偷,只要值最大)
class Solution {
public int rob(TreeNode root) {
int [] result = bp(root);
return Math.max(result[0],result[1]);
}
public int[] bp(TreeNode root){
if(root == null){
return new int[2];
}
int[] res =new int[2];
int[] left = bp(root.left);
int[] right = bp(root.right);
// res[0] = left[1]+right[1];
res[0] = Math.max(left[1],left[0])+Math.max(right[1],right[0]);
res[1] = root.val + left[0] +right[0];
return res;
}
}