打家劫舍问题

参考leetcode题解https://leetcode-cn.com/problems/house-robber-iii/solution/tong-yong-si-lu-tuan-mie-da-jia-jie-she-wen-ti-b-2/

LeetCode 「打家劫舍」系列问题(英文版叫 House Robber)是比较有代表性和技巧性的动态规划题目,今天就来聊聊这道题目。

打家劫舍系列总共有三道,难度设计非常合理,层层递进。第一道是比较标准的动态规划问题,而第二道融入了环形数组的条件,第三道更绝,把动态规划的自底向上和自顶向下解法和二叉树结合起来,我认为很有启发性。如果没做过的朋友,建议学习一下。

House Robber I

你是一个专业的盗贼,计划打劫某一条街一排的房屋。每个房屋都藏有一定的现金。但是如果两间相邻的房屋在同一晚被盗贼闯入的话,系统会自动警报。给定一个非负整数数组,计算在不触动警报装置的情况下,能够偷窃到的最高金额。例如:

输入:[1,2,3,1]
输出:4

解题思路

每到一个房间,有两个选择:1) 抢+继续抢下下个房间;2) 从下一个房间抢
两个选择中,每次都选更大的结果,最后得到的就是最多能抢到的。

// 递归思路
public int rob(int[] nums){
    return db(nums, 0);
}

int dp(int[] nums, int start){
    if(start>=nums.lengh)
        return 0;

    int res = max(dp(nums, start+1), nums[start]+dp(nums, start+2));
    return res;
}

// 使用备忘录,重复子问题不再计算
int[] memo;

int rob(int[] nums){
    memo = new int[nums.length];
    Arrays.fill(memo, -1);
    return dp(nums, 0);
}

int dp(int[] nums, int start){
    if(start>=nums.length)
        return 0;
    // 优先查找备忘录
    if(memo[start] != -1)
        return memo[start];
    int ret = max(dp(nums, start+1), nums[start]+dp(nums, start+2));

    // 记入备忘录
    memo[start] = ret;
    return ret;
}

// dp自底向上
int rob(int[] nums){
    int dp_i1 = 0, dp_i2 = 0, dp_i = 0;
    for(int i=nums.length-1; i>=0; i--){
        dp_i = max(nums[i]+dp_i2, dp_i1);
        dp_i1 = dp_i;
        dp_i2 = dp_i1;
    }
    return dp_i;
}

Hourse Robber II

题目与第一道描述基本一样,输入仍为一个数组,但房子是围成了一个圈。也就是说第一间房子和最后一间房子围成了一个圈。

解题思路

这里首尾的房子不能被同时抢,所以就有三种情况:都不被抢;最后一间房子不能抢;第一间房子不能抢。第一种情况被后面两种包含了,所以该题可拆成两个子问题,每个子问题都可由题1解决。

int rob(int[] nums) {
    int n = nums.length;
    if (n == 1) return nums[0];
    return Math.max(robRange(nums, 0, n - 2), 
                    robRange(nums, 1, n - 1));
}

// 仅计算闭区间 [start,end] 的最优结果
int robRange(int[] nums, int start, int end) {
    int n = nums.length;
    int dp_i_1 = 0, dp_i_2 = 0;
    int dp_i = 0;
    for (int i = end; i >= start; i--) {
        dp_i = Math.max(dp_i_1, nums[i] + dp_i_2);
        dp_i_2 = dp_i_1;
        dp_i_1 = dp_i;
    }
    return dp_i;
}

House Robber III

这里强盗面对的房子不是一排,不是一圈,而是一棵二叉树,每个节点都是一个房子,相连的房子不能同时被抢劫。


打家劫舍问题_第1张图片
image.png
int rob(TreeNode root){
    int[] res = dp(root);
    return max(res[0], res[1]);
}

// 返回一个大小为2的数组arr
// arr[0]表示不抢root的话,得到的最大收益
// arr[1]表示抢root的话,得到的最大收益
int [] dp(TreeNode root){
    if(root==null)
         return new int[]{0, 0};
    int [] left = dp(root.left);
    int right = dp(root.right);
    
    // 抢root
    int rob = root.val + left[0] + right[0];
    // 不抢root
    int not_rob = max(left[0], left[1]) + max(right[0], right[1]);

    return new int[]{not_rob, rob};
}

你可能感兴趣的:(打家劫舍问题)