• 打家劫舍系列(力扣198、213、337)Java动态规划


    目录

    一、打家劫舍(力扣198)

    二、打家劫舍II(力扣213)

    三、打家劫舍III(力扣337)


    一、打家劫舍(力扣198)

            198. 打家劫舍-力扣

            此题的动态规划有两种思路:

            1、思路一:

            参考309. 最佳买卖股票时机含冷冻期-力扣 ,我也写的有题解:

            买卖股票系列(力扣121、122、123、188、309、714) Java动态规划

            房屋只有两种状态:被偷 和 没被偷

            那么我们用dp数组来记录第i间房屋时的最大金额,dp[i][0] 记录被偷的情况,dp[i][1]记录没被偷的情况。

            dp[i][0]表示被偷,那么前一间必然没有被偷,当前值就是前一间没有被偷的情况下的金额+当前金额,则dp[i][0] = dp[i-1][1] + nums[i]

            dp[i][1]表示没被偷,那么前一间可能被偷,也可能没被偷,当前值就是这两种情况中较大的那个,则dp[i][1] = max{ dp[i-1][0] , dp[i][1] }

            最后取dp[len-1][0]和dp[len-1][1]的较大值即可。考虑到当前房屋值只与前一间房屋有关,则可以用变量来代替dp数组,就成了下面的代码。

            在买卖股票的题解(上面的链接)中有此方法更详细的讲解和推导过程,感兴趣的朋友可以去看一下。

    1. class Solution {
    2. public int rob(int[] nums) {
    3. int a = nums[0];
    4. int b = 0;
    5. for(int i=1; i
    6. int tempA = a;
    7. int tempB = b;
    8. a = tempB + nums[i];
    9. b = Math.max(tempA, tempB);
    10. }
    11. return Math.max(a, b);
    12. }
    13. }

            2、思路二

            对于第k间房屋,只有两种情况:被偷 和 不被偷

            如果被偷,当前金额为k-2间房屋的金额+当前金额

            如果不被偷,当前金额为k-1间房屋金额

            这间房屋的金额就是两个值中较大的那个,所以得到递推公式:

                                                    dp[i]=max(dp[i−2]+nums[i],dp[i−1])

    1. class Solution {
    2. static int max = 0;
    3. public int rob(int[] nums) {
    4. if(nums.length==1){
    5. return nums[0];
    6. }
    7. int c = nums[0];
    8. int b = Math.max(nums[0], nums[1]);
    9. int a = nums[0];
    10. for(int i=2; i
    11. a = Math.max(b, (c+nums[i]));
    12. c = b;
    13. b = a;
    14. }
    15. return Math.max(a, b);
    16. }
    17. }

    二、打家劫舍II(力扣213)

            213. 打家劫舍 II-力扣

             这题与上题的不同在于,最后一家与第一家连在了一起,而我们动态规划出来的结果是无法知道第一家是否被偷的。那么,我们是不是可以分而治之,分为第一家被偷第一家没被偷两种情况去看,这样问题就迎刃而解了。

            其中,偷东西的那部分代码可以抽取成方法,这样代码会更加简洁,但是分开写比较容易看清思路。 

    1. class Solution {
    2. public int rob(int[] nums) {
    3. int res;
    4. int len = nums.length;
    5. if(len == 1) {
    6. return nums[0];
    7. }
    8. if(len == 2) {
    9. return Math.max(nums[0], nums[1]);
    10. }
    11. //第一间偷了
    12. int a;
    13. int b;
    14. if(len==3) {
    15. res = nums[0];
    16. } else {
    17. a = nums[2];
    18. b = 0;
    19. for(int i=3; i1; i++) {
    20. int tempA = a;
    21. int tempB = b;
    22. a = tempB + nums[i];
    23. b = Math.max(tempA, tempB);
    24. }
    25. res = Math.max(a, b) + nums[0];
    26. }
    27. //第一间没偷
    28. a = nums[1];
    29. b = 0;
    30. for(int i=2; i
    31. int tempA = a;
    32. int tempB = b;
    33. a = tempB + nums[i];
    34. b = Math.max(tempA, tempB);
    35. }
    36. res = Math.max(res, Math.max(a, b));
    37. return res;
    38. }
    39. }

    三、打家劫舍III(力扣337)

            337. 打家劫舍 III-力扣

            这次要偷的房子组成了一棵二叉树,我们简化一下题目:二叉树的节点上有权值,要求不能同时取父子节点的权值,问最大权值和是多少?

            首先,每个节点有两个状态:被偷 和 没被偷。我们使用a(i)来表示第i个节点被偷时,他和他的子树所贡献的最大权值;使用b(i)表示第i个节点不被偷时,他和他的子树所贡献的最大权值。那么:

            因为a(i)表示被偷,所以他的子节点不能被偷,那么他的权值和是左右子树不被偷时的权值和+当前节点权值,即:

                                                    a(i) = b(i.left) + b(i.right) + i.val;

            因为b(i)表示当前节点不被偷,那么他的左右节点可以被偷也可以不被偷,我们取权值和较大的情况,即:

                                b(i) = max{a(i.left) , b(i.left)} + max{a(i.right) , b(i.right)}

            最终我们找出a(root) 和 b(root)中的较大值即可。a和b可考虑用哈希表来保存。

    1. /**
    2. * Definition for a binary tree node.
    3. * public class TreeNode {
    4. * int val;
    5. * TreeNode left;
    6. * TreeNode right;
    7. * TreeNode() {}
    8. * TreeNode(int val) { this.val = val; }
    9. * TreeNode(int val, TreeNode left, TreeNode right) {
    10. * this.val = val;
    11. * this.left = left;
    12. * this.right = right;
    13. * }
    14. * }
    15. */
    16. class Solution {
    17. Map a; //a.get(root) 表示当前节点被偷时,他和他的子树所能贡献的最大价值
    18. Map b; //b.get(root) 表示当前节点没被偷时,他和他的子树所能贡献的最大价值
    19. public int rob(TreeNode root) {
    20. a = new HashMap<>();
    21. b = new HashMap<>();
    22. dfs(root);
    23. return Math.max(a.get(root), b.get(root));
    24. }
    25. void dfs(TreeNode node) {
    26. if(node == null) {
    27. return;
    28. }
    29. dfs(node.left);
    30. dfs(node.right);
    31. a.put(node, b.getOrDefault(node.left, 0)+b.getOrDefault(node.right, 0)+node.val);
    32. b.put(node,
    33. Math.max(a.getOrDefault(node.left, 0), b.getOrDefault(node.left, 0))
    34. + Math.max(a.getOrDefault(node.right, 0), b.getOrDefault(node.right, 0)));
    35. }
    36. }

            不难看出,当前的ab值只与他的左右子树的ab值有关,所以,可以考虑将每次调用时的ab值返回给上一级调用,这样可以省去哈希表。

    1. /**
    2. * Definition for a binary tree node.
    3. * public class TreeNode {
    4. * int val;
    5. * TreeNode left;
    6. * TreeNode right;
    7. * TreeNode() {}
    8. * TreeNode(int val) { this.val = val; }
    9. * TreeNode(int val, TreeNode left, TreeNode right) {
    10. * this.val = val;
    11. * this.left = left;
    12. * this.right = right;
    13. * }
    14. * }
    15. */
    16. class Solution {
    17. public int rob(TreeNode root) {
    18. int []res = dfs(root);
    19. return Math.max(res[0], res[1]);
    20. }
    21. int[] dfs(TreeNode node) {
    22. if(node == null) {
    23. return new int[]{0, 0};
    24. }
    25. int []left = dfs(node.left);
    26. int []right = dfs(node.right);
    27. //表示当前节点被偷时,他和他的子树所能贡献的最大价值
    28. int a = left[1] + right[1] + node.val;
    29. //表示当前节点没被偷时,他和他的子树所能贡献的最大价值
    30. int b = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
    31. return new int[]{a, b};
    32. }
    33. }
  • 相关阅读:
    SDRAM学习笔记(MT48LC16M16A2,w9812g6kh)
    HTML本地离线缓存?
    【OpenCV-Python】教程:3-4 平滑去噪,高斯平滑,均值滤波,中值滤波
    DDD的简单落地实现
    JAVA计算机毕业设计医生在线诊所平台Mybatis+系统+数据库+调试部署
    第2章 Linux多进程开发 2.18 内存映射
    javaer你还在手写分表分库?来看看这个框架怎么做的 干货满满
    声纹技术(七):声纹技术的未来
    Qt之设置QLineEdit只能输入浮点数
    免疫沉淀常见问题解答 | MedChemExpress
  • 原文地址:https://blog.csdn.net/m0_49499183/article/details/127986941