代码随想录算法训练营第四十八天|198.打家劫舍、213.打家劫舍II、337.打家劫舍III

89 阅读3分钟

198.打家劫舍

题目链接:198.打家劫舍

思路:动态规划五步曲:

  1. dp[i] 表示小于i包括i在内的房屋,最多可以偷窃的金额为dp[i]
  2. dp[i]有两种方式可以得出,不偷第i家,为dp[i - 1],偷第i家,为dp[i - 2] + nums[i]

所以递推公式为:dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);

  1. 初始化,dp[0] = nums[0], dp[1] = Math.max(nums[0], nums[1])
  2. 从前向后遍历即可。
  3. 举例说明
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

题目链接: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

题目链接:337.打家劫舍III

思路:本题一定是要后序遍历,因为需要根据递归的返回值来做下一步计算。本题是一道树形dp的题目,因为需要在树的结构上进行状态转移。以递归三部曲为主,融合动态规划五步曲进行分析。

  1. 确定返回值,递归参数。

返回一个长度为2的数组,这个数组就是dp数组,dp[0],代表偷当前节点所得价值,dp[1]代表不透当前节点所得价值。递归参数就是节点。

  1. 确定返回条件

遇到空节点,无论偷与不偷都是返回0,这里也相当于dp数组的初始化。

  1. 遍历顺序,采用后序遍历。

  2. 确定单层逻辑

如果偷了当前节点,那么,左右孩子就都不能偷,res[0] = node.val + left[1] + right[1];

如果不偷当前节点,那么就可以偷左右孩子,但是还要选出最大的,res[1] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);

  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;
    }
}