代码随想录Day50

198.打家劫舍

题目:198. 打家劫舍 - 力扣(LeetCode)

思路:感觉就是要么从1开始,要么从2开始,间隔起来相加就行

dp[i]表示经过i个房屋,能偷到的最高金额

dp[i] += dp[i-2]

尝试(递推公式不会)
class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        int[] dp = new int[len+1];
        for(int i=2; i<=len; i++){
            dp[i] += nums[i-2];
        }
        int[] dp2 = new int[len+1];
        for(int i=3; i<=len; i++){
            dp2[i] += nums[i-2];
        }
        return Math.max(dp[len],dp2[len]);
    }
}
答案
// 动态规划
class Solution {
	public int rob(int[] nums) {
		if (nums == null || nums.length == 0) return 0;
		if (nums.length == 1) return nums[0];

		int[] dp = new int[nums.length];
		dp[0] = nums[0];
		dp[1] = Math.max(dp[0], nums[1]);
		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];
	}
}
小结
  1. dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。
  2. 如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i],如果不偷第i房间,那么dp[i] = dp[i - 1]
  3. 递推公式:dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
  4. 从递推公式可以看出,基础是dp[0]和dp[1]

213.打家劫舍||

题目:213. 打家劫舍 II - 力扣(LeetCode)

思路:多了一个情况,就是最后一个和第一个房间,只能选一个来偷,,我需要处理的情况就是,在偷完第一个房间后,就不能偷最后一个,但是我不知道dp[i]是处于哪个状态

尝试(思路没错,但是只能部分AC)
class Solution {
    public int rob(int[] nums) {
        if(nums == null || nums.length ==0 ) return 0;
        if(nums.length == 1 ) return nums[0];

        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0],nums[1]);
        int max = nums.length;
        if(max%2 ==1){
            if(nums[0] > nums[1]) max--;
        }
        for(int i = 2; i <max; i++){
            dp[i] = Math.max(dp[i-2] + nums[i],dp[i-1]);
        }
        return dp[nums.length-1];
    }
}
答案
// x,y,z
class Solution {
    public int rob(int[] nums) {
        if (nums == null || nums.length == 0)
            return 0;
        int len = nums.length;
        if (len == 1)
            return nums[0];
        return Math.max(robAction(nums, 0, len - 1), robAction(nums, 1, len));
    }

    int robAction(int[] nums, int start, int end) {
        int x = 0, y = 0, z = 0;
        for (int i = start; i < end; i++) {
            y = z;// y相当于dp[i-1]
            z = Math.max(y, x + nums[i]); // x相当于dp[i-2]
            x = y;
        }
        return z;
    }
}

 

// dp数组

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

    int robAction(int[] nums, int start, int end) {
        if (end - start == 1) return nums[start]; // 如果只有一个房子,直接返回该房子的金额

        int[] dp = new int[end - start];
        dp[0] = nums[start];
        dp[1] = Math.max(nums[start], nums[start + 1]);

        for (int i = start + 2; i < end; i++) {
            dp[i - start] = Math.max(dp[i - start - 1], dp[i - start - 2] + nums[i]);
        }

        return dp[end - start - 1];
    }
}

 

小结
  1. 分为偷第一个房子和偷最后一个房子两种情况
  2. 关键是要写一个函数,传入指定下标,返回偷到的最大金额,在打家劫舍基础上,dp数组计算式下标减去start就行
  3. 递推公式:dp[i - start] = Math.max(dp[i - start - 1], dp[i - start - 2] + nums[i]);

213.打家劫舍|||

题目:337. 打家劫舍 III - 力扣(LeetCode)

思路:每个节点都要进行判断,三个节点时,选两个子节点相加,或者是选父节点,2个节点时,选其中最大的一个,从最下面往上遍历,每个节点都进行聚合,把最大值聚合起来往上传,我的代码有问题,如何选择了子节点的子节点,那么当前子节点的值要直接加在root上面

尝试()

class Solution {
    public int rob(TreeNode root) {
        int left=0,right=0;
        if(root.left != null)  left = rob(root.left);
        if(root.right != null)  right = rob(root.right);
        if(root.left !=null && root.right !=null){
            return Math.max(root.val,right+left);
        }
        if(root.left !=null){
            return Math.max(root.val,left);
        }
        if(root.right !=null){
            return Math.max(root.val,right);
        }
        return root.val;
    }
}
答案
class Solution {
    public int rob(TreeNode root) {
        int[] res = robAction1(root);
        return Math.max(res[0], res[1]);
    }

    int[] robAction1(TreeNode root) {
        int[] res = new int[2];
        if (root == null)
            return res;

        int[] left = robAction1(root.left);
        int[] right = robAction1(root.right);

        res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        res[1] = root.val + left[0] + right[0];
        return res;
    }
}
    // 3.状态标记递归
    // 执行用时:0 ms , 在所有 Java 提交中击败了 100% 的用户
    // 不偷:Max(左孩子不偷,左孩子偷) + Max(右孩子不偷,右孩子偷)
    // root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) +
    // Math.max(rob(root.right)[0], rob(root.right)[1])
    // 偷:左孩子不偷+ 右孩子不偷 + 当前节点偷
    // root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val;
小结
  • 把情况分为【当前节点偷】和【当前节点不偷】两种情况,用【 root[0] 】与【 root[1] 】
  • 不偷当前节点,也就是偷两个子节点,公式为:
res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
  • 偷当前节点,就需要放弃两个子节点
 res[1] = root.val + left[0] + right[0];
  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值