代码随想录算法训练营day48|198.打家劫舍,213.打家劫舍II,337.打家劫舍III

198.打家劫舍

力扣

思路:

1. 物品:每个房间的现金,背包:金额;01背包

2. dp数组及其下标含义:dp[i]表示到房间i时所偷窃的最高金额为dp[i];

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

4. 初始化:dp[0] = nums[0],dp[1] = Math.max(nums[0],nums[1]);【注意,题目中说nums.length>=1,当nums长度为1时直接返回】

5. 遍历顺序:dp[i]的值依赖dp[i-1]和dp[i-2],因此从前往后遍历;

class Solution {
	public int rob(int[] nums) {
        int len = nums.length;
		if (len==1) return nums[0];
		int[] dp = new int[len];
		dp[0] = nums[0];
		dp[1] = Math.max(dp[0], nums[1]);
		for (int i=2; i<len; i++) {
			dp[i]=Math.max(dp[i-1], dp[i-2]+nums[i]);
		}
		return dp[len-1];
	}
}

213.打家劫舍II

力扣

思路:

1. 基本同上;

2. 区别:分为从i=0开始偷,偷到i=nums.length-1,和从i=1开始偷,偷到i=nums.length-2这两种情况;返回两种情况下金额更大的那个;

class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if (len == 1) return nums[0];
        return Math.max(robAction(nums, 0, len - 1), robAction(nums, 1, len));
    }
    public int robAction(int[] nums, int start, int end) {
        int x = 0, y = 0, z = 0;//相当于只维护dp[i],dp[i-1]和dp[i-2]
        for (int i=start; i<end; i++) {
            y = z;//更新y
            z = Math.max(y, x + nums[i]);
            x = y;//更新x
        }
        return z;
    }
}

337.打家劫舍III

力扣 

思路:动态规划(树形dp)

1. 首先思考遍历方式:前中后序(深度优先搜索)还是层序遍历(广度优先搜索)。本题一定是后序遍历,因为需要通过递归函数的返回值做下一步计算

2. 如果抢了当前节点,那么两个孩子就不能动;如果没抢当前节点,就「考虑」抢左右孩子。

3. 确定递归函数的参数和返回值:需要使用一个长度为2的数组,记录当前节点偷与不偷所得到的最大金钱,因此返回值为长度为2的int[]【即dp数组,dp[0]记录不偷该节点所得到的的最大金钱,dp[1]记录偷该节点所得到的的最大金钱。】

4. 终止条件:当遇到空节点时,返回 new int[]{0,0}。

5. 遍历顺序:后序遍历;递归左节点,用left[]记录左节点偷和不偷的情况。递归右节点,用right[]记录右节点偷和不偷的情况。

6. 单层递归的逻辑:若果偷当前节点,那么左右孩子就不能偷,因此dp[1] = cur.val + left[0] + right[0];如果不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷,那一定是选一个最大的,因此dp[0] = Math.max(left[0], left[1]) + max(right[0], right[1]);最后头节点返回偷和不偷两种情况中金钱最多的那一个;

class Solution {
    public int rob(TreeNode root) {
        int[] res = robAction(root);
        return Math.max(res[0],res[1]);
    }
    public int[] robAction(TreeNode root){
        int[] dp = new int[2];
        if(root==null) return dp;
        int[] left = robAction(root.left);
        int[] right = robAction(root.right);
        dp[0] = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
        dp[1] = root.val+left[0]+right[0];
        return dp;
    }
}

思路:记忆化递归

class Solution {
    Map<TreeNode,Integer> memo = new HashMap<>();
    public int rob(TreeNode root) {
        return robAction(root);
    }
    public int robAction(TreeNode root){
        if(root==null) return 0;
        if(memo.containsKey(root)) return memo.get(root);
        int money = root.val;
        if(root.left!=null){
            money+=robAction(root.left.left)+robAction(root.left.right);
        }
        if(root.right!=null){
            money+=robAction(root.right.left)+robAction(root.right.right);
        }
        int res = Math.max(money,robAction(root.left)+robAction(root.right));
        memo.put(root,res);
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值