2021-04-15 打家劫舍系列(198, 213, 337) [动态规划]

198. 打家劫舍

思路一:

  • dp[i]表示洗劫第i家能获得的最大收益。
  • tmp用来表示[0,i-2]中能获得的最大收益。
    d p [ i ] = n u m s [ i ] + m a x j ∈ [ 0 , i − 2 ] ( d p [ j ] ) r e s = m a x i ∈ [ 0 , n ] d p [ i ] dp[i]=nums[i]+max_{j\in[0,i-2]}(dp[j]) \\ res = max_{i \in [0,n]} dp[i] dp[i]=nums[i]+maxj[0,i2](dp[j])res=maxi[0,n]dp[i]
class Solution {
    public int rob(int[] nums) {
        if(nums.length==0) return 0;
        if(nums.length==1) return nums[0];
        if(nums.length==2) return Math.max(nums[0],nums[1]);
        int[] dp = new int[nums.length]; // dp[i]表示洗劫第i家最大能获得的收益
        dp[0] = nums[0];
        dp[1] = nums[1];
        int res = Math.max(dp[0],dp[1]);
        int tmp = dp[0]; // 用来记录[0,i-2]最多赚的钱
        for(int i=2;i<nums.length;i++){
            dp[i] = tmp+nums[i];
            tmp = Math.max(tmp,dp[i-1]);
            res = Math.max(res,dp[i]);
        }
        return res;
    }
}

思路二:

  • dp[i]表示洗劫[0,i]中能获得的最大收益
  • 情况一:抢第i家
    • dp[i] = nums[i] + dp[i-2]
    • 这里有一个问题是dp[i-1]只是说[0,i-1]中能获得的最大收益,并未说明我们一定偷了第i-1家。但这种担心是多余的
      • 假设我们偷了第i-1家,则我们上面的式子满足要求
      • 假设我们没有偷第i-1家,则由情况二:dp[i-1] = dp[i-2],我们上面的式子也满足要求。
  • 情况二:不抢第i家
    • dp[i] = dp[i-1]

d p [ i ] = m a x ( n u m s [ i ] + d p [ i − 2 ] , d p [ i − 1 ] ) dp[i] = max(nums[i]+dp[i-2],dp[i-1]) dp[i]=max(nums[i]+dp[i2],dp[i1])

class Solution {
    public int rob(int[] nums) {
        int pre = 0, cur = 0, tmp; //pre i-2 cur i-1 tmp nums[i]
        for(int num : nums) {
            tmp = cur;
            cur = Math.max(pre + num, cur);
            pre = tmp;
        }
        return cur;
    }
}

213. 打家劫舍 II

思路:

  • 关键是我们可以将问题拆分为打劫[0,n-2]和[1,n-1]家,再求两者的最大值。

原因在于:

  • 如果我们打劫了0,由于限制条件,剩下能打劫的房屋一定在[2,n-2]中选择了,这一定<[0,n-2]能获得的值。
  • 如果我们没有打劫0,由于限制条件,剩下能打劫的房屋一定在[1,n-1]中选择了,这一定<[1,n-1]。

同理对n-1也可以有类似的讨论。

class Solution {
    public int rob(int[] nums) {
        if(nums.length==1) return nums[0];
        return Math.max(aidRob(nums,0,nums.length-1),aidRob(nums,1,nums.length));
    }

    // dp[i] = max(dp[i-1],dp[i-2]+nums[i])
    // tmp = max(cur,pre+num);
    public int aidRob(int[] nums,int l, int r){
        int pre=0,cur=0,tmp;
        for(int i=l;i<r;i++){
            tmp = Math.max(cur,pre+nums[i]);
            pre = cur;
            cur = tmp;
        }
        return cur;
    }
}

337. 打家劫舍 III [树形dp]

  • 这里的dp是树形的。
    在这里插入图片描述
class Solution {
    HashMap<TreeNode,Integer> f = new HashMap<>();
    HashMap<TreeNode,Integer> g = new HashMap<>();
    public int rob(TreeNode root) {
        dfs(root);    
        return Math.max(f.getOrDefault(root,0),g.getOrDefault(root,0));
    }

    public void dfs(TreeNode root){
        if(root == null)
            return;
        dfs(root.left);
        dfs(root.right);
        // 先到最底部,一路更新上来
        f.put(root,root.val+g.getOrDefault(root.left,0)+g.getOrDefault(root.right,0));
        g.put(root,Math.max(f.getOrDefault(root.left,0),g.getOrDefault(root.left,0))+Math.max(f.getOrDefault(root.right,0),g.getOrDefault(root.right,0)));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值