当前位置: 首页 > news >正文

代码随想录算法训练营第47天 || 198.打家劫舍 || 213.打家劫舍II || 337.打家劫舍III

代码随想录算法训练营第47天 || 198.打家劫舍 || 213.打家劫舍II || 337.打家劫舍III

198.打家劫舍

题目介绍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。偷窃到的最高金额 = 2 + 9 + 1 = 12 。

个人思路

动规五部曲

  1. 确定dp数组及其下标含义

    dp[j]:从下标0到下标j的房间中,能偷的最大金额

  2. 确定递推公式

    dp[i] = Integer.max(dp[i - 1], dp[i - 2] + nums[i]);

    表示偷或不偷当前房间对应的金额,其中取较大的那个;

  3. 初始化dp数组

    dp[0] = nums[0];
    dp[1] = Integer.max(dp[0], nums[1]);
    

    适应递推公式,从而初始化前两个dp数组元素

  4. 确定遍历顺序

    正序遍历即可(结合递推公式)

  5. 打印dp数组检验

代码:

class Solution {public int rob(int[] nums) {if (nums.length == 1)return nums[0];int[] dp = new int[nums.length];dp[0] = nums[0];dp[1] = Integer.max(dp[0], nums[1]);for (int i = 2; i < nums.length; i++) {dp[i] = Integer.max(dp[i - 1], dp[i - 2] + nums[i]);}return dp[dp.length - 1];}
}

213.打家劫舍II

题目介绍

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。

示例 3:

输入:nums = [1,2,3]
输出:3

思路解析

这道题看似只加了一个循环条件,但确实比较难考虑了不少。我一开始就想通过做标记的办法,决定最后加不加最后一个元素,过程中还得判断加与不加相等的情况(并且尽量取能加最后一个元素的情况)。但是发现会出现一种情况,最后一个元素大于第一个元素,但根据前面的取法,最后一位已经取不到了,导致答案错误。然后我就想能不能一开始比较始末位置的值,决定从大的一边去遍历,但这样要写两套相似的逻辑代码(当然可以函数封装,遍历+1/-1用一个变量存储即可),不过还是较为麻烦

题解解析

这个问题大致可以分为三种情况

  • 去掉头尾的非环问题
  • 去掉头的非环问题
  • 去掉尾的非环问题

实际上情况2、3已经包括1了,因为dp[j]:表示的就是从头到j下标的最大偷盗价值,如果边界取不到,那就刚好等同于情况一。所以本题,我们只需要考虑后两种情况,然后取最大值即可(封装函数,调用两次,比较两个结果)

动规五部曲

  1. 确定dp数组及其下标含义

    dp[j]:前j+1家能偷到的最大价值

  2. 确定递推公式

    dp[i] = Integer.max(dp[i - 1], dp[i - 2] + nums[i+left]);
    

    还是与之前一样,偷与不偷当前房子的比较

  3. 初始化dp数组

    dp[0] = nums[left];
    dp[1] = Integer.max(dp[0], nums[left+1]);
    
  4. 确定遍历顺序

    正序遍历即可

  5. 打印dp数组检验

代码:

class Solution {public int rob(int[] nums) {if (nums.length == 1)return nums[0];int rob_max1 = rob_(nums, 1, nums.length - 1);int rob_max2 = rob_(nums, 0, nums.length - 2);return Integer.max(rob_max1,rob_max2);}public int rob_(int[] nums,int left,int right){if (right - left == 0)return nums[left];int[] dp = new int[right-left+1];dp[0] = nums[left];dp[1] = Integer.max(dp[0], nums[left+1]);for (int i = 2; i < dp.length; i++) {dp[i] = Integer.max(dp[i - 1], dp[i - 2] + nums[i+left]);}return dp[dp.length - 1];}
}

337.打家劫舍III

题目介绍

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root

除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AM0FWPIY-1677057886054)(null)]

输入: root = [3,2,3,null,3,null,1]
输出: 7 
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7

示例 2:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ubZdCCUF-1677057886010)(null)]

输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9

思路解析

结合动态规划和二叉树遍历的知识,每个结点都有偷和不被偷两种情况,那么我们分别记录每个节点的这两种情况的最优解,由下往上返回最优解,从子树最优得到全局最优(有点小贪心吧)

动规五部曲、递归三部曲

  1. 确定参数及返回值

    参数:结点

    返回值:int[2],偷与不偷当前结点的最大金额

  2. 确定终止条件

    if (root == null)return new int[]{0, 0};
    
  3. 确定递归顺序

    后序遍历

  4. 确定单层递归逻辑

    1. 确定dp数组及其下标含义

      dp[0]:表示已当前结点为根的二叉树中,当前结点的最大金额

      dp[1]:表示已当前结点为根的二叉树中,不偷当前结点的最大金额

    2. 确定递推公式

      dp[0] = root.val + dp_left[1] + dp_right[1];//偷自己的情况下,子树只能取不偷的情况
      //不偷自己的情况下,取偷或不偷左右孩子的最大值
      dp[1] = Integer.max(dp_left[0], dp_left[1]) + Integer.max(dp_right[0], dp_right[1]);
      
  5. 最后取偷或不偷根节点的最大金额即可

class Solution {public int rob(TreeNode root) {int[] dp = traversal(root);return Integer.max(dp[0], dp[1]);}public int[] traversal(TreeNode root) {if (root == null)return new int[]{0, 0};int[] dp_left = traversal(root.left);int[] dp_right = traversal(root.right);int[] dp = new int[2];//0表示偷自己,1表示不偷自己dp[0] = root.val + dp_left[1] + dp_right[1];//不偷自己的情况下,取偷或不偷左右孩子的最大值dp[1] = Integer.max(dp_left[0], dp_left[1]) + Integer.max(dp_right[0], dp_right[1]);return dp;}
}

t.right);
int[] dp = new int[2];//0表示偷自己,1表示不偷自己
dp[0] = root.val + dp_left[1] + dp_right[1];
//不偷自己的情况下,取偷或不偷左右孩子的最大值
dp[1] = Integer.max(dp_left[0], dp_left[1]) + Integer.max(dp_right[0], dp_right[1]);
return dp;
}
}


http://www.lryc.cn/news/16583.html

相关文章:

  • JVM调优方式
  • 机器学习模型监控的 9 个技巧
  • Linux 实现鼠标侧边键实现代码与网页的前进、后退
  • 健身蓝牙耳机推荐,推荐五款适合健身的蓝牙耳机
  • Type-c诱骗取电芯片大全
  • Scala模式匹配详解(第八章:基本语法、模式守卫、模式匹配类型)(尚硅谷笔记)
  • Linux:基于libevent读写管道代码
  • 2022年中职网络安全逆向题目整理合集
  • Tencent OS下逻辑卷(LVM)增加硬盘扩容
  • 【Java】Spring的创建和使用
  • 【HTML】HTML 表单 ④ ( textarea 文本域控件 | select 下拉列表控件 )
  • MySQL 操作 JSON 数据类型
  • 关于vue3生命周期的使用、了解以及用途(详细版)
  • 2月,真的不要跳槽。
  • Vulnhub靶场----4、DC-4
  • 51单片机学习笔记_12 LCD1602 原理及其模块化代码
  • 科技 “新贵”ChatGPT 缘何 “昙花一现” ,仅低代码风靡至今
  • redis基本入门| 怎么安装redis?什么的是redis?怎么使用?
  • kubernetes traefik ingress 安装部署以及使用和注意点
  • 电脑病毒已灭绝,是真的吗?
  • 基于RK3399+Linux QT地面测试台多参数记录仪测试平台软件设计(二)
  • 追梦之旅【数据结构篇】——详解C语言实现动态版顺序栈
  • Ubuntu 使用Nohup 部署/启动/关闭程序
  • Spring 用到了哪些设计模式
  • Linux上基于PID找到对应的进程名以及所在目录
  • jvm知识点与面试题
  • 算法前缀和—Java版
  • 拨开迷雾 看见vivo穿越周期的秘密
  • 浅谈常用的日志框架
  • 字节是真的难进,测开4面终上岸,压抑5个月,终于可以放声呐喊