LeetCode 198 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
1.确定dp数组以及下标含义:dp[i]表示偷到第i号房屋偷到的最高金额。
2.递推公式:
如果不偷窃i号房屋,那么dp[i]=dp[i-1];
如果偷窃i号房屋,那么dp[i]=dp[i-2]+nums[i-1]。
所以。
3.初始化:初始化dp[0]=0,dp[1]=nums[0]即可。
4.确定遍历顺序:从前往后遍历。
5.举例推导dp数组:
代码如下:
class Solution {
public int rob(int[] nums) {
int n=nums.length;
int[] dp=new int[n+1];
dp[0]=0;
dp[1]=nums[0];
for(int i=2;i<=n;i++){
dp[i]=Math.max(dp[i-2]+nums[i-1],dp[i-1]);
}
return dp[n];
}
}
LeetCode 213 打家劫舍II
题目链接:213. 打家劫舍 II - 力扣(LeetCode)
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:输入:nums = [1,2,3]
输出:3
分两种情况:
考虑偷窃1号房屋,那么就要把最后一个房屋排除在外;
考虑偷窃最后一个房屋,那么就要把1号房屋排除在外。
最后看那种情况可以偷窃到的金额最大。
对每种情况可以偷窃到的最大金额使用打家劫舍的动态规划法去求,代码如下:
class Solution {
public int rob(int[] nums) {
if(nums.length==1)return nums[0];
int res=0;
res=Math.max(range(nums,0,nums.length-2),range(nums,1,nums.length-1));
return res;
}
public int range(int[] nums,int start,int end){
int n=end-start+1;
int[] dp=new int[n+1];
dp[0]=0;
dp[1]=nums[start];
for(int i=2;i<=n;i++){
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[start+i-1]);
}
return dp[n];
}
}
LeetCode 337 打家劫舍III
题目链接:337. 打家劫舍 III - 力扣(LeetCode)
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 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
对每一个节点node,都设置一个dp数组,数组大小为2,dp[0]表示不偷node可以获得的最大金额;dp[1]表示偷窃node可以获得的最大金额。
如果node为空,那么dp[0]=dp[1]=0.
采用后序遍历。通过递归左子节点,得到偷或不偷左子节点可以获得的最大总金额;通过递归右子节点,得到偷或不偷右子节点可以获得的最大总金额。
如果偷窃当前node节点,那么node的左子节点和右子节点都不可以再偷,所以dp[0]=node.val+left[0]+right[0];
如果不偷窃当前node节点,那么可以考虑偷窃node的左子节点和右子节点,所以dp[1]=max(left[0],left[1])+max(right[0],right[1])。
int[] left=robTree(node.left);
int[] right=robTree(node.right);
res[0]=Math.max(left[0],left[1])+Math.max(right[0],right[1]);//不抢node
res[1]=node.val+left[0]+right[0];//抢node
最后返回根节点dp[0]和dp[1]的最大值就是最终结果。
举例推导(以示例1为例):用后序遍历的方式推导dp数组:
整体代码如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int rob(TreeNode root) {
int[] res=robTree(root);
return Math.max(res[0],res[1]);
}
public int[] robTree(TreeNode node){
int[] res=new int[2];
if(node==null){
res[0]=res[1]=0;
return res;
}
int[] left=robTree(node.left);
int[] right=robTree(node.right);
res[0]=Math.max(left[0],left[1])+Math.max(right[0],right[1]);//不抢node
res[1]=node.val+left[0]+right[0];//抢node
return res;
}
}