代码随想录-2023/08/14

132 阅读2分钟

动态规划

198.打家劫舍

解题思路:

动态规划

  1. 定义dp数组含义: dp[i]代表当前位置i的最大价值
  2. 若偷当前位置, 则其最大价值等于dp[i-2]
  3. 若不偷当前位置, 则最大位置等于dp[i-1]

代码:

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if(n < 2) return nums[0];
        // 动态规划 --- 每个位置两个状态 偷 | 不偷 的最大价值
        int[][] dp = new int[n][2];
        dp[0][0] = 0;
        dp[0][1] = nums[0]; 
        dp[1][0] = nums[0];
        dp[1][1] = nums[1];
        for(int i=2; i < n; i++){
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]);
            dp[i][1] = Math.max(dp[i-2][0], dp[i-2][1]) + nums[i];
        }
        return Math.max(dp[n-1][0], dp[n-1][1]);
    }
}

213.打家劫舍II

解题思路:

两次打家劫舍I

  1. 若偷第一间, 则最后一间不能偷, 对(0~n-1)进行打家劫舍
  2. 若不偷第一间, 则最后一间可以偷, 对(1~n)进行打家劫舍

代码:

class Solution {
    // 分情况讨论: 偷第一间房子还是不偷第一间房子
    // 打家劫舍II---转换成两个打家劫舍I
    public int rob(int[] nums) {
        int n = nums.length;
        if(n < 2) return nums[0];
        if(n < 3) return Math.max(nums[0], nums[1]);

       // 1.偷第一间屋子, 则最后一间屋子不能偷, 遍历范围就到 (0-n-1)
       int[] dp_1 = new int[n-1];
       dp_1[0] = nums[0]; dp_1[1] = nums[0];
       for(int i=2; i<n-1; i++){
            dp_1[i] = Math.max(dp_1[i-1], dp_1[i-2]+nums[i]);
       }

       // 2.不偷第一间屋子, 则最后一间屋子可以偷, 则遍历范围为 (1-n)
       int[] dp_2 = new int[n];
       dp_2[0] = 0;
       dp_2[1] = nums[1]; dp_2[2] = Math.max(nums[1], nums[2]);
       for(int i=3; i<n; i++){
            dp_2[i] = Math.max(dp_2[i-1], dp_2[i-2]+nums[i]);
       }

       // 3.返回这两种的最大值
       return Math.max(dp_1[n-2], dp_2[n-1]);
    }
}

337.打家劫舍III

动态规划之树形dp

每个节点都有两个状态---偷与不偷, 且其状态都由子节点推导而来, 故需要后序递归遍历二叉树

  1. 当遍历到null时, 返回{0, 0}
  2. 当不为null时候, 需要用数组接收子节点的状态
  3. 得到子节点的状态后, 需要对当前节点进行处理
  4. 分别判断偷与不偷的价值是多少, 并返回

代码:

class Solution {
    // 树形DP
    public int rob(TreeNode root) {
        int[] ans = dfs(root);
        return Math.max(ans[0], ans[1]);
    }
    // dp数组含义-当前节点状态: dp[0]: 偷, dp[1]: 不偷
    public int[] dfs(TreeNode root){
        if(root == null) return new int[]{0, 0};
        int[] left = dfs(root.left);
        int[] right = dfs(root.right);
        // 偷当前节点 - 子节点不能偷
        int rob = root.val + left[1] + right[1];
        // 不偷当前节点, 那么子节点偷不偷取决于偷不偷的金额多少
        int noRob = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        return new int[]{rob, noRob};
    }
}