Day 42 算法记录|动态规划 09 (打家劫舍)

打家劫舍

198.打家劫舍

1.dpi:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dpi

2.dpi = max(dpi - 2 + numsi, dpi - 1);

3.初始化,dp0 和 dp1,dp0 一定是 nums0,dp1 = max(nums0, nums1);

3.遍历顺序,dpi 是根据dpi - 2 和 dpi - 1 推导出来的,那么一定是从前到后遍历!

java 复制代码
class Solution {
    public int rob(int[] nums) {
        if(nums ==null||nums.length ==0) return 0;
        if(nums.length ==1) return nums[0];

        int len = nums.length;
        int[] dp = new int[len];
        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[len-1];
    }
}

进一步对滚动数组的空间优化 dp数组只存与计算相关的两次数据

因为当前能偷的最多与上一家还有上上一家有关

java 复制代码
class Solution {
    public int rob(int[] nums) {
   
        if(nums.length ==1) return nums[0];

        int len = nums.length;
        int[] dp = new int[2];
        dp[0]=nums[0]; //上上家
        dp[1] = Math.max(nums[0],nums[1]); // 上一家
        int res =0;

        for(int i =2;i<nums.length;i++){
            res = Math.max(dp[1],dp[0]+nums[i]); //当前家的最大值
            dp[0] = dp[1];
            dp[1] = res;

        }

    return dp[1];
    }
}

213.打家劫舍II

这个视频讲解的不错

这道题与上一道不同的是首尾相连

那么第一个和最后一个一定只能选择其中一个

java 复制代码
class Solution {
    public int rob(int[] nums) {
      if(nums.length == 1) return nums[0];
      int n = nums.length;
    int res = Math.max(robAction(Arrays.copyOfRange(nums,0,n-1)),robAction(Arrays.copyOfRange(nums,1,n)));
    return res;

    }

    public int robAction(int [] nums){
        if(nums.length ==1) return nums[0];

        int[] dp = new int[2];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0],nums[1]);
        int temp =0;
        for(int i=2;i<nums.length;i++){
            temp = Math.max(dp[1],dp[0]+nums[i]);
            dp[0] = dp[1];
            dp[1] = temp;
        }
        return dp[1];
    }
}

337.打家劫舍 III

每个节点是否被选中,取决于该节点的左右子节点是否被选
方法一 :递归

从根节点开始遍历,分为两种情况,偷当前节点和孙子节点,不偷当前节点,偷孩子

再对每个结点比较大小

java 复制代码
class Solution {
    // 1.递归去偷,超时
    public int rob(TreeNode root) {
        if (root == null)
            return 0;
        int money = root.val;
        if (root.left != null) {
            money += rob(root.left.left) + rob(root.left.right);
        }
        if (root.right != null) {
            money += rob(root.right.left) + rob(root.right.right);
        }
        return Math.max(money, rob(root.left) + rob(root.right));
    }
    }

上面的递归超时,因为出现了很多重复的步骤,改进记忆法递归
方法二 :递归去偷,记录状态

从根节点开始遍历的时候,就用一个类似数组的集合进行存放,减少遍历次数

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
    Map<TreeNode,Integer> memo = new HashMap<>();
    return robAction(root,memo);
    }

    public int robAction(TreeNode root, Map<TreeNode,Integer> memo ){
       //停止条件
       if(root == null) return 0;

       // 避免重复操作
        if(memo.containsKey(root)){
            return memo.get(root);
        }

      
        //1.选择当前节点 再偷孙子
          int cur = root.val;
        if(root.left !=null){
            cur +=robAction(root.left.left,memo)+robAction(root.left.right,memo);
        }

        if(root.right != null){
            cur +=robAction(root.right.left,memo) + robAction(root.right.right,memo);
        }
        //2.不偷当前节点,去偷孩子
        int  cur1 = robAction(root.left,memo)+robAction(root.right,memo);
        //比较上面两种选择
        int res = Math.max(cur,cur1);
        memo.put(root,res);
        return res;
    }
}

方法三: 状态标记递归

每个节点是否选择,取决于其左右子节点(所以只需要记录临近节点的状态,其余就不用管,就像上一道里面的上一个,上上个)

dp0,dp1分别表示当前节点没有被选中,和被选中

left0,left1表示其左孩子没选中和选中,right同样

对于当前节点:
不偷:Max(左孩子不偷,左孩子偷) + Max(又孩子不偷,右孩子偷),我不偷,我的孩子可偷可不偷

root0 = Math.max(rob(root.left)0, rob(root.left)1) +Math.max(rob(root.right)0, rob(root.right)1)

偷:左孩子不偷+ 右孩子不偷 + 当前节点偷,我偷,孩子必然不能偷

java 复制代码
class Solution {
    public int rob(TreeNode root) {
     int[] dp = robAction(root);
     return Math.max(dp[0],dp[1]);
    }

    public int[] robAction(TreeNode root ){
       //停止条件
       if(root == null) return new int[2];

        
       int[] left = robAction(root.left);
       int[] right = robAction(root.right);
       int[] dp = new int[2]; 

       dp[0] = Math.max(left[0],left[1])+Math.max(right[0],right[1]);//当前节点没有被选中
       dp[1] = root.val + left[0] + right[0];//被选中
        return dp;
    }
}
相关推荐
通信小呆呆2 小时前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人
benben0443 小时前
强化学习之DQN算法族(基于gymnasium开发)
算法
小小工匠4 小时前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾4 小时前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
何以解忧,唯有..4 小时前
Go语言循环语句详解:for、range与循环控制
开发语言·算法·golang
想吃火锅10055 小时前
【leetcode】88.合并两个有序数组js
算法
生成论实验室5 小时前
机器人:一个自主运动的系统
人工智能·算法·语言模型·机器人·自动驾驶·agi·安全架构
Qres8215 小时前
算法复键——树状数组
数据结构·算法
H178535090965 小时前
SolidWorks第四部分_直接实体建模特征9_替换面原理
线性代数·算法·机器学习·3d建模·solidworks