198.打家劫舍
思路:动态规划五步曲:
- dp[i] 表示小于i包括i在内的房屋,最多可以偷窃的金额为dp[i]
- dp[i]有两种方式可以得出,不偷第i家,为dp[i - 1],偷第i家,为dp[i - 2] + nums[i]
所以递推公式为:dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
- 初始化,dp[0] = nums[0], dp[1] = Math.max(nums[0], nums[1])
- 从前向后遍历即可。
- 举例说明
class Solution {
public int rob(int[] nums) {
// dp[i] 表示小于i包括i在内的房屋,最多可以偷窃的金额为dp[i]
int[] dp = new int[nums.length];
// 递推公式:dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
// 初始化dp[0] = nums[0]
if (nums.length == 1) return nums[0];
dp[0] = nums[0];
dp[1] = nums[1] > nums[0] ? nums[1] : nums[0];
// 遍历顺序
for (int i = 2; i < nums.length; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[nums.length - 1];
}
}
213.打家劫舍II
思路:只需要考虑两种情况,偷的时候包含第一家但是不包含最后一家,包含最后一家但是不包含第一家,其他思路与上一题相同。
class Solution {
public int rob(int[] nums) {
// 考虑两种情况,包含第一家不包含最后一家,包含最后一家不包含第一家
if (nums.length == 1) return nums[0];
return Math.max(robAction(nums, 0, nums.length - 2), robAction(nums, 1, nums.length - 1));
}
public int robAction(int[] nums, int start, int end) {
if (end - start == 0) return nums[start];
int[] dp = new int[end - start + 1];
dp[0] = nums[start];
dp[1] = Math.max(nums[start], nums[start + 1]);
int j = 2;
for (int i = start + 2; i <= end; i++) {
dp[j] = Math.max(dp[j - 1], dp[j - 2] + nums[i]);
j++;
}
return dp[end - start];
}
}
337.打家劫舍III
思路:本题一定是要后序遍历,因为需要根据递归的返回值来做下一步计算。本题是一道树形dp的题目,因为需要在树的结构上进行状态转移。以递归三部曲为主,融合动态规划五步曲进行分析。
- 确定返回值,递归参数。
返回一个长度为2的数组,这个数组就是dp数组,dp[0],代表偷当前节点所得价值,dp[1]代表不透当前节点所得价值。递归参数就是节点。
- 确定返回条件
遇到空节点,无论偷与不偷都是返回0,这里也相当于dp数组的初始化。
-
遍历顺序,采用后序遍历。
-
确定单层逻辑
如果偷了当前节点,那么,左右孩子就都不能偷,res[0] = node.val + left[1] + right[1];
如果不偷当前节点,那么就可以偷左右孩子,但是还要选出最大的,res[1] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
- 举例说明
class Solution {
public int rob(TreeNode root) { // 树形dp,后序递归
// 一个二维的dp数组,代表偷当前节点和不偷当前节点所盗取的最高金额
int[] res = robAction(root);
return Math.max(res[0], res[1]);
}
int[] robAction(TreeNode node) {
int[] res = new int[2];
if (node == null) return res;
int[] left = robAction(node.left);
int[] right = robAction(node.right);
res[0] = node.val + left[1] + right[1];
res[1] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
return res;
}
}