代码随想录刷题笔记 Day 52 | 打家劫舍 No.198 | 打家劫舍 II No.213 | 打家劫舍III No.337

文章目录

    • Day 52
      • 01. 打家劫舍(No. 198)
        • <1> 题目
        • <2> 笔记
        • <3> 代码
      • 02. 打家劫舍 II(No. 213)
        • <1> 题目
        • <2> 笔记
        • <3> 代码
      • 03.打家劫舍III(No. 337)
        • <1> 题目
        • <2> 笔记
        • <3> 代码

Day 52

01. 打家劫舍(No. 198)

题目链接

代码随想录题解

<1> 题目

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

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

示例 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 <= nums.length <= 100
  • 0 <= nums[i] <= 400
<2> 笔记

打家劫舍问题算是力扣上比较知名的一类动态规划的题目,但其实有了前面背包问题的铺垫,做起来也没有那么难。

题目中讲的是一个小偷去偷沿街的房屋,但是相邻两个房屋不能同时被偷,否则就会触发警报,如何选择打劫的房屋能够使得小偷得到的钱最多。

先来看一下本题的状态,既然题目中问的是偷哪些房屋能获得的钱最多,那状态暂时确定成偷这些房屋能获得的最多的钱是多少。

再来思考一下这个状态能否转移,本题唯一的限制就是相邻的两个房屋不能同时偷,如果要确定现在偷的这个房屋的状态,必须要知道前面的房屋有没有被偷,如果前面的房屋被偷了,那这个状态一定就是上一次的状态(因为不能连着偷嘛)。

所以对于一个房屋应该规定两个状态:

  • 这个房屋被偷的话,最多能获得多少钱。
  • 这个房屋没有被偷的话,最多能获得多少钱。

有了这两个部分就可以推出后面的状态了,如果这个房屋不被偷的话,那前面房屋是否被偷就不会产生影响的,这时候最大值就是前面房屋被偷状态和不被偷状态取一个最大值。

如果这个房屋被偷的话,那一定是建立在前面的房屋没有被偷的状态的基础上,即前面房屋没有被偷的状态加上偷这个房屋的价格。

这两个状态 同时 都要被保存,因为后面的状态需要依靠这两个状态去推出。

确定了一个可以转移的状态,来明确一下 dp 数组的含义,因为要同时存储两个状态,所以 dp 应该被声明为一个二维数组,dp[i][0] 表示这个房屋没有被偷的话,最大值是多少,而
dp[i][1] 表示这个房屋如果被偷的话,最大值是多少。

再来看递推公式,有两个状态也就对应着两个递推公式:

  • dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);
  • dp[i][1] = dp[i - 1][0] + nums[i];

如果不好理解的话,可以去回顾一下 dp 数组的含义

dp 数组的初始化:题目中说了 nums.length >= 1 所以可以放心大胆的使用 nums[0] 来作为初始化的条件,当然将数组扩充一下,初始化打劫 0 个房屋的情况也是可以的。

dp[0][0] = 0;
dp[0][1] = nums[0];

现在就可以写出代码了。

<3> 代码
class Solution {public int rob(int[] nums) {int n = nums.length;int[][] dp = new int[n][2];dp[0][0] = 0;dp[0][1] = nums[0];for (int i = 1; i < nums.length; i++) {dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);dp[i][1] = dp[i - 1][0] + nums[i];}return Math.max(dp[n - 1][0], dp[n - 1][1]);}
}

02. 打家劫舍 II(No. 213)

题目链接

代码随想录题解

<1> 题目

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

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

示例 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 <= nums.length <= 100
  • 0 <= nums[i] <= 1000
<2> 笔记

本题与上一题的区别就是 最后一个和第一个不能同时取得,除此之外就没有任何差异了。

所以大致可以把本题分为三种情况:

  • 第一个和最后一个都不要
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 要第一个但是不要最后一个
    在这里插入图片描述

  • 要最后一个不要第一个
    在这里插入图片描述

注意这里说的要或者不要不是说一定会去偷第一家或者最后一家,而是要不要考虑偷这两家的情况,所以第一种情况其实是包含在第二种和第三种情况之中的,所以只需要求最后两种情况,然后返回其中的最大值即可。

public int rob(int[] nums) {if (nums.length == 1) return nums[0];int res1 = getRes(0, nums.length - 2, nums);int res2 = getRes(1, nums.length - 1, nums);return Math.max(res1, res2);
}

这段代码就是先求得后两种情况的值,然后去取最大值,下面来看一下 getRes 方法的具体实现,为了限制遍历的范围,传入了一个 startIndex 和一个 endIndex,这个下标指的是元素在 nums 中的下标,所以在确认遍历遍历返回的时候,要保证遍历下标 一定要包含 endIndex,如果我们将 dp 数组初始化为这样:dp[endIndex - startInde][2],然后将遍历的范围设置为 [startIndex, dp.length] 会导致计算结果错误的原因,因为这样并不会遍历到 endIndex这个下标。

所以要将 dp 数组的行长度初始化为 dp[endIndex + 1] 然后对 dp[startIndex] 的部分进行初始化,这样就可以保证能够准确规范遍历范围。

int[][] dp = new int[endIndex + 1][2];
dp[startIndex][0] = 0;
dp[startIndex][1] =  nums[startIndex];
<3> 代码
class Solution {public int rob(int[] nums) {if (nums.length == 1) return nums[0];int res1 = getRes(0, nums.length - 2, nums);int res2 = getRes(1, nums.length - 1, nums);return Math.max(res1, res2);}public int getRes(int startIndex, int endIndex, int[] nums) {int[][] dp = new int[endIndex + 1][2];dp[startIndex][0] = 0;dp[startIndex][1] =  nums[startIndex];for (int i = startIndex + 1; i <= endIndex; i++) {dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);dp[i][1] = dp[i - 1][0] + nums[i];}return Math.max(dp[dp.length - 1][0], dp[dp.length - 1][1]);}
}

03.打家劫舍III(No. 337)

题目链接

代码随想录题解

<1> 题目

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

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

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

示例 1:

在这里插入图片描述

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

示例 2:

在这里插入图片描述

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

提示:

  • 树的节点数在 [1, 104] 范围内
  • 0 <= Node.val <= 104
<2> 笔记

本题是二叉树和动态规划融合的题目;一个节点有两种选择:取或者不取,且只有 父节点 被取的情况下才会影响子节点的状态。

和上面的题目相同,声明一个数组来保存本结点取和不取的情况,因为本题采用的是递归的方式,每一层递归都会存储一个 dp 数组,所以直接将数组声明为一维数组,来存储本层的情况即可。

递推公式和上面相同,但本题牵扯到了二叉树,所以这里要考虑遍历顺序,中序遍历一般二叉搜索树才会用到,暂时不考虑,来考虑用前序遍历还是后序遍历。

  • 前序遍历,就是在进入节点的时候就对节点的值进行处理,所以需要得知上一个节点是否被取得,但是前序遍历存在一个问题:因为本题中如果父节点没有被取的话,那左节点和右节点是可以 同时 被取得的,但是前序遍历在进入左子树和进入右子树之前 没有规范自己到底取没取(因为传入的是dp 包含了两种情况),所以对于以上的情况解决起来比较困难,比如说这样:

    int[] dpLeft = reversal(node.left, lastDP);
    int[] dpRight = reversal(node.right, lastDP);
    

    当递归回到这个节点,也就是第一个递归得到返回值的时候,在本层 无法确定 这个返回值是在取得本层节点还是在没有取得本层节点的情况下退出来的,就无法按照逻辑再对后面的内容进行规范。

  • 后序遍历,而后序遍历就恰好适用于这种需要得到子节点状态的情况,对于一个节点来说,如果要取得本节点,那就是子节点都不取的情况,将 left[0] + right[0] 即可得到结果,如果不取本节点,拿得到的结果就是 Math.max(left[0], left[1]) + Math.max(right[0], right[1])

通过后序遍历就可以保证数组是符合逻辑的;通过本题再次回顾一下后序遍历的使用时机,也就是父节点需要得到子节点状态的时候,所以后序遍历的代码一般是有返回值的。

<3> 代码
class Solution {List<Integer> path = new ArrayList<>();public int rob(TreeNode root) {int[] res = reversal(root);return Math.max(res[0], res[1]);}public int[] reversal(TreeNode node, int[] lastDP) {if (node == null) {return new int[]{0, 0};}int[] dp = new int[2];int[] dpLeft = reversal(node.left, lastDP);int[] dpRight = reversal(node.right, lastDP);return dp;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/747697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

H5/微信 Video标签移动端播放问题

一、禁止/阻止/取消默认的全屏播放 亲测&#xff1a; IOS和安卓均有效 <video x5-playsinline"true"playsinline"true"webkit-playsinline"true"x-webkit-airplay"true"x5-video-orientation"portraint"><source…

工智能的迷惑是技术发展的产物

简述&#xff1a; 随着ChatGPT在全球科技舞台上掀起一股热潮&#xff0c;人工智能再次成为了人们关注的焦点。各大公司纷纷紧跟潮流&#xff0c;推出了自己的AI大模型&#xff0c;如&#xff1a;文心一言、通义千问、讯飞星火、混元助手等等&#xff0c;意图在人工智能领域占据…

sqlplus登录卡死无响应异常处理

一、问题描述 通过Sqlplus 访问数据hang死在登录界面&#xff0c;且不能通过CtrlC取消&#xff0c;如下所示&#xff1a; [oracletest01 ~]$ sqlplus / as sysdbaSQL*Plus: Release 19.0.0.0.0 - Production on Fri Mar 13 10:41:36 2024 Version 19.3.0.0.0Copyright (c) 198…

Vue多文件学习项目综合案例——购物车,黑马vue教程

一、项目截图 二、主要知识点 vuex的使用json-server的使用json-server --watch index.json三、需要注意的点 json-server 安装成功&#xff0c;查看版本直接报错。安装默认版本埋下的一个坑&#xff0c;和node版本不匹配作者直接安装vuex&#xff0c;默认安装也是版本不匹配…

C语言案例01, 输入两个整数,获得两个数加减乘除的值,持续更新中~

一.题目 /* 输入两个整数,获得两个数加减乘除的值 */ *///头文件 #include <stdio.h>//主方法 int main() {//声明两个int类型的变量int a;int b;//提示用用户输入printf("请输入两个整数!\n");//用scanf 获取a和b在内存中的值scanf("%d%d",&a…

ai智能外呼机器人的功能,机器人对话常用语模板搭建

智能外呼机器人就是用来往外呼出打电话的;经常看到有文章说电话机器人将要代替传统人工话务员、电话销售员要失业了、外呼机器人要颠覆电销革命了等等&#xff0c;我想说的是&#xff0c;目前市场上的电话机器人还远远不能达到&#xff0c;未来几年内也不一定会实现。 下面就简…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Panel)

可滑动面板&#xff0c;提供一种轻量的内容展示窗口&#xff0c;方便在不同尺寸中切换。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 说明&#xff1a; 子组件类型&a…

QMI8658芯片I2C驱动开发指南

这个芯片纯国产挺好用的&#xff0c;电路很好设计&#xff0c;我这垃圾焊功&#xff0c;纯手焊&#xff0c;&#xff0c;居然能用。 第一部分 硬件连接 画的很简陋&#xff0c;看看就可以了&#xff0c;这里I2C总线需要接10K上拉没有画出来&#xff0c;这个需要注意一下。 …

树形结构 一篇文章梳理

树形结构是一种非常重要的非线性数据结构&#xff0c;它模拟了具有层次关系的数据模型。在树形结构中&#xff0c; 目录 一、组成元素&#xff1a; 二、树的属性&#xff1a; 深度或高度 度 路径 路径长度 三、树的类型 1 二叉树 2 多叉树 3 完全二叉树 4 满二叉树…

【计算机网络_传输层】UDP和TCP协议

文章目录 1. 重新理解端口号端口号划分netstat指令pidof 2. UDP协议2.1 UDP协议端格式2.2 UDP的特点2.3 UDP的注意事项2.4 基于UDP的应用层协议 3. TCP协议&#xff08;传输控制协议&#xff09;3.1 TCP协议的格式和报头字段3.2 如何解包和分用3.3 理解TCP协议报头3.4 TCP协议的…

day-20 二叉树的层序遍历

思路&#xff1a;利用队列进行广度优先遍历即可 注意点&#xff1a;ArrayList执行remove之后&#xff0c;索引i会立即重排&#xff0c;注意可能越界 code: /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeN…

[Java、Android面试]_05_内存泄漏和内存溢出

本人今年参加了很多面试&#xff0c;也有幸拿到了一些大厂的offer&#xff0c;整理了众多面试资料&#xff0c;后续还会分享众多面试资料。 整理成了面试系列&#xff0c;由于时间有限&#xff0c;每天整理一点&#xff0c;后续会陆续分享出来&#xff0c;感兴趣的朋友可关注收…

Xcode调试Qt 源码

在Mac下使用Xcode 开发Qt程序&#xff0c;由于程序断点或者崩溃后&#xff0c;Qt库的堆栈并不能够正确定位到源码的cpp文件&#xff0c;而是显示的是汇编代码&#xff0c;导致不直观的显示。 加载的其他三方库都是同理。 所以找了攻略和研究后&#xff0c;写的这篇文章。 一&a…

SIP调试之SIPP测试工具

SIPP是针对SIP协议的一个性能测试的命令行工具&#xff0c;可以动态显示测试的统计信息&#xff08;如呼叫速率、延时、消息统计等&#xff09;。用户可以通过XML场景配置文件&#xff0c;自定义模拟各种UAC/UAS测试场景的信令交互流程&#xff0c;可以被用来测试IP话机、SIP代…

【IC设计】Verilog线性序列机点灯案例(二)(小梅哥课程)

文章目录 该系列目录&#xff1a;设计目标设计思路RTL 及 Testbench仿真结果存在的问题&#xff1f;改善后的代码RTL代码testbench代码 仿真结果 案例和代码来自小梅哥课程&#xff0c;本人仅对知识点做做笔记&#xff0c;如有学习需要请支持官方正版。 该系列目录&#xff1a;…

Nexus如何导入jar以及批量导入Maven的本地库目录

上传依赖包到Nexus 服务器的方式有多种, 包含: 1.单个jar上传: 在Nexus管理台页面上传单个jar 2.源码编译上传:在源码项目中使用 Maven的deploy 命令发布 3. 使用脚本批量上传Maven本地库的目录 前言 本篇基于 Nexus 的版本是 nexus-3.55.0-01本方法适用Linux和WindowsWind…

C++中using 和 typedef 的区别

C中using 和 typedef 的区别_typedef using-CSDN博客 在C中&#xff0c;“using”和“typedef”执行声明类型别名的相同任务。两者之间没有重大区别。C中的“Using”被认为是类型定义同义词。此方法也称为别名声明。定义这些别名声明的工作方式类似于使用“using”语句定义C中…

MySQL-HMA 高可用故障切换

本章内容&#xff1a; 了解MySQL MHA搭建MySQL MHAMySQL MHA故障切换 1.案例分析 1.1.1案例概述 目前 MySQL 已经成为市场上主流数据库之一&#xff0c;考虑到业务的重要性&#xff0c;MySQL 数据库 单点问题已成为企业网站架构中最大的隐患。随着技术的发展&#xff0c;MHA…

【四 (2)数据可视化之 Matplotlib 常用图表及代码实现 】

目录 文章导航一、介绍二、安装Matplotlib三、导入Matplotlib四、设置可以中文显示四、常用图形1、散点图&#xff08;Scatter Plot&#xff09;2.1、线性图&#xff08;Line Plot&#xff09;2.2、堆叠折线图2.3、多图例折线图3.1、柱状图/条形图&#xff08;Bar Chart&#x…

PostgreSQL中vacuum 物理文件truncate发生的条件

前言 前段时间&#xff0c;有些同学说到vacuum截断的行为时&#xff0c;认为&#xff0c;只要末尾是空页&#xff0c;无论多少&#xff0c;都会被截断&#xff0c;真是这样的吗&#xff1f; PostgreSQL当中&#xff0c;由于vacuum的操作并不总能将死元组的空间进行”物理截断…