day47

1、leetcode198 打家劫舍

  1. 动规五步法

    1. dp[i]:偷盗房屋序号为i的房屋时,能偷窃到的最高金额
    2. 递归公式
      • 偷第i间房:dp[i] = dp[i-2] + nums[i]
      • 不偷第i间房:dp[i] = dp[i-1]
      • dp[i] = max(dp[i-2] + nums[i], dp[i-1])
    3. 初始化
      1. dp[0] = nums[0]
      2. dp[1] = max(nums[0], nums[1])
    4. 遍历顺序
      • 从前向后遍历
    5. 举例
  2. 代码

    class Solution {
        public int rob(int[] nums) {
            if(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]);
    
            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];
            
        }
    }
    

2、leetcode213 打家劫舍Ⅱ

  1. 首尾房间不能同时被抢

    1. 情况一:首尾房间都不被抢
    2. 情况二:第一间房间被抢,最后一间不抢
    3. 情况三:第一间房间不抢,最后一间被抢
    4. 综上:最终抢劫金额,应为三种情况的最大值
      • 情况一肯定是结果最小,只需比较情况二和情况三就行
        • 因为这两种情况对于房⼦的选择余地⽐情况⼀⼤,房⼦⾥的钱数都是⾮负数,所以选择余地⼤,最终结果肯定大于情况一
  2. 代码

    class Solution {
        public int rob(int[] nums) {
            if(nums.length == 1) return nums[0];
            int res1 = robRange(nums, 0, nums.length - 2);
            int res2 = robRange(nums, 1, nums.length - 1);
            int res = Math.max(res1, res2);
            return res;
        }
    
        public int robRange(int[] nums, int start, int end) {//[start, end]闭区间
            if( start == end) return nums[start];
            int[] dp = new int[nums.length];
            dp[start] = nums[start];
            dp[start + 1] = Math.max(nums[start], nums[start+1]);
    
            for(int i=start+2; i<=end; i++) {
                dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
            } 
    
            return dp[end];
        }
    }
    

3、leetcode337 打家劫舍Ⅲ

  1. 递推法

    class Solution {
        Map<TreeNode, Integer> memo = new HashMap<>();//保存计算后的结果
    
        public int rob(TreeNode root) {
            if(root == null) return 0;
            if(memo.containsKey(root)) return memo.get(root);
    
            //偷父节点,然后去下下家偷
            int val1 = root.val;
            if(root.left != null) {
                val1 += rob(root.left.left) + rob(root.left.right);  // 跳过root->left
            }
            if(root.right != null) {
                val1 += rob(root.right.left) + rob(root.right.right); // 跳过root->right
            }
    
            //不偷父节点,偷其左右孩子节点
            int val2 = rob(root.left) + rob(root.right);// 考虑root的左右孩子
            
            int res = Math.max(val1, val2);
            memo.put(root, res); //memo记录结果
            return res;
        }
    }
    
  2. 动态规划

    class Solution {
        public int rob(TreeNode root) {
            int[] res = robTree(root);
            return Math.max(res[0], res[1]);
        }
    
        public int[] robTree(TreeNode cur) {
            //下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。
            int res[] = new int[2];
            if (cur == null){
                return res;
            }
    
            int[] left = robTree(cur.left);//左
            int[] right = robTree(cur.right);//右
    
            //中
            res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); //不偷cur【左右孩子可以偷,但是具体要不要偷,看值的大小】
            res[1] = cur.val + left[0] + right[0];//偷cur【左右孩子不能偷】
            return res;
    
        }
    }
    
posted @ 2023-03-02 16:24  黄三七  阅读(19)  评论(0)    收藏  举报