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

news2025/1/16 14:09:19

文章目录

    • 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.coloradmin.cn/o/1519167.html

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

相关文章

RocketMQ学习笔记三(黑马)大神级

课程来源:6.RocketMQ安装_哔哩哔哩_bilibili (时长:19.5h) 讲解版本:4.5版本(我是以4.8版本实践的) 目录 第一部分 核心功能 第1章 RocketMQ的下载、安装、启动和测试(Linux环境) 启动: 测试: 第2章 RocketMQ集群搭建 2.1 集群特点 2.2 集群模式 2.3 双主…

HTML_CSS练习:HTML注释

一、代码示例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>HTML注释</title> </head> <body><marquee loop"1">马龙强<!--下面的输入框是可以滚动的&#x…

新手必看!YouTube运营教程:你需要的策略、工具和技巧

欢迎来到 YouTube 的世界&#xff01;对于跨境电商和海外社媒营销人员来说&#xff0c;YouTube 是一个充满机遇的宝地。无论你是希望通过视频内容扩大品牌影响力&#xff0c;还是想要抓住全球买家的注意力&#xff0c;掌握YouTube运营的基础是你成功的第一步。本篇教程将为你揭…

云与云计算:从传统到云端的IT资源变革

云&#xff1a;从分散到集约&#xff0c;资源服务化的新模式 让我们先通过一个生活化的场景来理解“云”这一概念。几十年前&#xff0c;诸如农村地区的居民需要自给自足&#xff0c;比如在自家院子里打井取水&#xff0c;冬季烧煤取暖&#xff0c;一切满足自己生活需要的都要…

健康消费需求持续上涨,品牌如何抓住机遇

近年来&#xff0c;国内“大健康”时代徐徐展开&#xff0c;居民消费热度不断攀升&#xff0c;主动健康消费已经成为新的增长点&#xff0c;然而健康行业的内容由于专业性较高&#xff0c;传播范围有限导致无法直接触达用户。在当下的传播环境中&#xff0c;品牌应该如何抓住机…

百万级excel导入导出

项目目录结构&#xff1a; * 导入思路&#xff1a; excel拆分为多个sheet&#xff0c;开启20个线程分别处理20个sheet&#xff0c;采用批量插入的方式入库 * 导出思路&#xff1a; 开启20个线程分页读取数据&#xff0c;放入到map中&#xff0c;CountDownLatch保证并发安全…

C++(14)——vector

目录 vector是什么&#xff1f; vector的使用 vector的构造 vector iterator的使用 vector空间增长问题 vector的增删查改 push_back和pop_back operator[] 总结 vector是什么&#xff1f; vector是什么呢&#xff1f;按照英文来说&#xff0c;vector的英文是向量、矢…

Lord 3DMCV7-AHRS 时间同步硬件触发设置

目的:通过FPGA发送脉冲触发IMU采集数据。FPGA发送脉冲时,IMU才有数据产生。 FPGA与IMU的硬件接线就不讲了,这里主要说明的是IMU的设置以及ROS驱动的config文件更改。 1. WIN上位机设置 通过IMU在WINDOWS的上位机SensorConnect对IMU的GPIO、波特率等基本功能进行设值,具体…

视频素材软件app免费网站哪里找?

在如今的短视频时代&#xff0c;为了满足视频素材创作者们的需求&#xff0c;有很多提供免费短视频素材下载的应用程序可供选择。 接下来&#xff0c;我们将对视频素材软件app免费网站哪里找做出介绍 可以考虑以下几个视频素材软件app免费 蛙学网 提供多元化视频&#xff0c…

面试常问:你在项目中遇到了哪些比较棘手的问题?怎么解决的?

你在项目中遇到了哪些比较棘手的问题?怎么解决的&#xff1f;这个问题是面试官经常会问的一个问题。 如果你回答我在项目中没有怎么遇到&#xff0c;那么面试官会觉得你什么都不会&#xff0c;对项目了解也不够深入也没有负责什么项目。 面试官其实还挺关心的是应聘者的问题…

注意力机制Attention、CA注意力机制

一、注意力机制 产生背景&#xff1a; 大数据时代&#xff0c;有很多数据提供给我们。对于人来说&#xff0c;可以利用重要的数据&#xff0c;过滤掉不重要的数据。那对于模型来说&#xff08;CNN、LSTM&#xff09;&#xff0c;很难决定什么重要、什么不重要&#xff0c;因此…

APP开发如何成功上架到应用市场?

在当今移动互联网时代&#xff0c;开发一款优秀的APP并将其成功上架到各大应用市场是每个开发者梦寐以求的成就之一。但是&#xff0c;不同类型的APP在上架过程中可能会面临各种不同的挑战和要求。 APP上架到应用市场一般包括以下几个流程&#xff1a; 步骤一&#xff1a;开发及…

web部署 三

案例: 1/在其中一个网站目录下创建\software文件夹&#xff0c;里面放txtppt/mp4/iso,文件类型。 2/web站点目录浏览启动应用。 3/用win10客户机浏览software目录下文件&#xff0c;并下载。txtppt/mp4/iso&#xff0c;发现问题并解决。 首先我们先建立一个software的文件夹并…

Linux控制台、终端、Shell 的历史

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; ①微思网络&#xff0c;始于2002年&#xff01;专注IT认证培训22年。 ② 领取学习资料/课程咨询&#xff1a;小美老师&#xff08;wx&#xff09;&…

工作中Git如何切换远程仓库地址

工作中Git如何切换远程仓库地址 部门之前的仓库不用了&#xff0c;重新建了一个仓库&#xff0c;但是上传代码还是上传到了之前的仓库里面了&#xff0c;所以得进行修改&#xff0c;下面将修改地址的方法进行操作。 方法一、直接修改远程仓库地址 查看当前远程仓库地址 git …

【图形界面】学生宿舍信息管理系统,简单,模板框架,含完整代码

目录 开发一个学生宿舍管理系统 概述 开发环境 程序设计 功能展示 主菜单 添加学生信息界面 删除学生信息界面 修改学生信息界面 查询学生信息界面 5. 完整代码 6. 总结 开发一个学生宿舍管理系统 在本文中&#xff0c;我们将介绍如何使用Python和Tkinter库开发一…

P1149 [NOIP2008 提高组] 火柴棒等式

题目描述 给你 &#xfffd;n 根火柴棍&#xff0c;你可以拼出多少个形如 &#xfffd;&#xfffd;&#xfffd;ABC 的等式&#xff1f;等式中的 &#xfffd;A、&#xfffd;B、&#xfffd;C 是用火柴棍拼出的整数&#xff08;若该数非零&#xff0c;则最高位不能是 00&…

【MySQL】4. 表的操作

表的操作 1. 创建表 语法&#xff1a; CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎;说明&#xff1a; field 表示列名 datatype 表示列的类型 character set 字符集&#xff0c…

iOS面试题锦集

1. 问&#xff1a;一张图片所占内存大小跟什么有关&#xff1f; 图片所占内存大小&#xff0c;与图片的宽高有关 我们平时看到的png、jpg、webp这些图片格式&#xff0c;其实都是图片压缩格式。通过对应的算法来优化了大小以节省网络传输与本地保存所需的资源。 但是当我们加…

Nuxt3配置本地访问链接 -- Network: use --host to expose

这种表示没有在线访问本地链接 在nuxt.config.ts里面 export default defineNuxtConfig({// 配置本地访问链接devServer: {host: 192.168.1.41,//自己电脑的Ipport: 3000,}, }) 复制到浏览器就可以访问&#xff0c;给别人使用必须是局域网&#xff0c;同一个网络