美文网首页leetcode
打家劫舍问题

打家劫舍问题

作者: geaus | 来源:发表于2020-02-09 17:14 被阅读0次

    参考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

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


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

    相关文章

      网友评论

        本文标题:打家劫舍问题

        本文链接:https://www.haomeiwen.com/subject/gwxnxhtx.html