24暑假算法刷题 | Day39 | 动态规划 VII | LeetCode 198. 打家劫舍,213. 打家劫舍 II,337. 打家劫舍 III

目录

  • 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 <= nums.length <= 100
  • 0 <= nums[i] <= 400

题解

动态规划解决。根据题意,每次要考虑的无非是“要不要抢当前这家”,如果要抢,则肯定不能抢前一家;否则,要考虑抢前一家。

⚠️ 注意这里只是 考虑 抢前一家,不是一定要抢,因为可能出现间隔两家抢到的总金额更高的情况。

  • dp 数组含义: dp[i] 表示抢前 i 家,能抢到的最高金额

  • 状态转移方程: dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])

    如果抢当前这家,即抢一个 nums[i] ,则不能抢前一家,所以“衔接”的是再前面一家的情况,即 dp[i - 2] ——注意这里仍不表示一定会抢第 i - 2 家,只是利用之前已求得的抢前 i - 2 家能获得的最高金额。

    如果不抢当前这家,则直接用前一家的结果,即 dp[i - 1]

    上述两种取较大的结果,即 dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])

代码(C++)

int rob(vector<int>& nums) {if (nums.size() == 1)return nums[0];vector<int> dp(nums.size());dp[0] = nums[0];dp[1] = max(nums[0], nums[1]);for (int i = 2; i < nums.size(); ++i) dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);return dp[nums.size() - 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 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

题解

这题和 198. 打家劫舍 差不多,只不过数组变成了循环结构。所以最大的变化就是“首尾相连”了,而根据题目,每次要考虑的仍然是“相邻两家”不能同时抢,即数组的一头一尾不能一起抢,其他的其实和原版一致。

因此,既然首尾不能同时出现,我们不妨直接分别考虑 “有头无尾”“无头有尾” 两种情况,再取其中较大的结果即可。

代码(C++)

class Solution
{
private:// 普通的打家劫舍int robRange(const vector<int> &nums, int start, int end){if (end == start)return nums[start];vector<int> dp(end - start + 1);dp[0] = nums[start];dp[1] = max(nums[start], nums[start + 1]);for (int i = 2; i < dp.size(); ++i)dp[i] = max(dp[i - 2] + nums[start + i], dp[i - 1]);return dp[end - start];}public:int rob(vector<int> &nums){if (nums.size() == 1)return nums[0];return max(robRange(nums, 0, nums.size() - 2), robRange(nums, 1, nums.size() - 1));}
};

337. 打家劫舍 III

点此跳转题目链接

题目描述

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 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

题解

由于二叉树的结构,不难想到从根节点往下递归搜索的方法,每次考虑抢当前节点、不抢孩子节点和不抢当前节点、可能抢孩子节点的情况即可。同时,用一个哈希表存储已经求过的节点的最大金额,实现记忆化,降低时间开销:

class Solution1I // 记忆化的递归暴力搜索
{
private:unordered_map<TreeNode*, int> valueMap; // 记忆化:记录搜索过的节点public:int rob(TreeNode *root){if (!root)return 0; // 空节点,没法抢if (!root->left && !root->right)return root->val; // 没有左右孩子,直接抢当前节点// 有记忆:直接采用求过的该节点能偷的最大金额if (valueMap.find(root) != valueMap.end())return valueMap[root];// 偷当前节点(父节点),则不能偷左右孩子,但考虑孙子节点int v1 = root->val;if (root->left)v1 += rob(root->left->left) + rob(root->left->right);if (root->right)v1 += rob(root->right->left) + rob(root->right->right);// 不偷当前节点(父节点),则可以考虑左右孩子节点int v2 = rob(root->left) + rob(root->right);valueMap[root] = max(v1, v2); // 记录当前节点能偷的最大金额return valueMap[root];}
};

此外,还可以采用动态规划的方法。本题是我做的第一道树形结构中运用动态规划的题目,参考了 Carl的解决方案 ,感觉很巧妙:

1️⃣ 确定递归函数的参数和返回值

这里我们要求一个节点 偷与不偷的两个状态所得到的金钱,那么返回值就是一个长度为2的数组。

参数为当前节点,代码如下:

/// @brief 抢或不抢当前节点root,在以root为根的子树中能获得的最大金额
/// @param root(当前节点)
/// @return 大小为2的数组dp,dp[0]表示不偷root能得最大金额,dp[1]表示偷
vector<int> tryRob(TreeNode *root) {

其实这里的返回数组就是 dp 数组。

所以 dp 数组的含义:

  • 下标为0记录(绝对)不偷该节点所得到的的最大金钱
  • 下标为1记录(考虑)偷该节点所得到的的最大金钱

所以本题 dp 数组就是一个长度为2的数组!

那么长度为2的数组怎么标记树中每个节点的状态呢?

别忘了在递归的过程中,系统栈会保存每一层递归的参数

如果还不理解的话,就接着往下看,看到代码就理解了。

2️⃣ 确定终止条件

在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回:

vector<int> dp(2, 0);
if (!root) return dp; // 空节点,抢不抢都没钱

这也相当于 dp 数组的初始化。

3️⃣ 确定遍历顺序

首先明确的是使用后序遍历。 因为要通过递归函数的返回值来做下一步计算。

通过递归左节点,得到左节点偷与不偷的金钱。

通过递归右节点,得到右节点偷与不偷的金钱。

代码如下:

// 下标0:不偷,下标1:偷
vector<int> left = tryRob(root->left); // 抢或不抢左孩子
vector<int> right = tryRob(root->right); // 抢或不抢右孩子
// 中

4️⃣ 确定单层递归的逻辑

如果不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷一定是选一个最大的,所以: dp[0] = max(left[0], left[1]) + max(right[0], right[1]);

如果是偷当前节点,那么左右孩子就不能偷,dp[1] = root->val + left[0] + right[0];如果对下标含义不理解就再回顾一下 dp 数组的含义

最后当前节点的状态就是 {dp[0], dp[1]} ; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}

代码如下:

// 不抢当前节点,则其子节点可抢可不抢
dp[0] = max(left[0], left[1]) + max(right[0], right[1]);// 抢当前节点,则其子节点绝对不能抢
dp[1] = root->val + left[0] + right[0];

5️⃣ 举例推导dp数组

以示例1为例, dp 数组状态如下:(注意用后序遍历的方式推导

在这里插入图片描述

最后头节点就是 取下标0 和 下标1的最大值就是偷得的最大金钱

整体代码如下:

class Solution // 动态规划
{
private:/// @brief 抢或不抢当前节点root,在以root为根的子树中能获得的最大金额/// @param root(当前节点)/// @return 大小为2的数组dp,dp[0]表示不抢root能得最大金额,dp[1]表示抢vector<int> tryRob(TreeNode *root) {vector<int> dp(2, 0);if (!root)return dp; // 空节点,抢不抢都没钱vector<int> left = tryRob(root->left); // 抢或不抢左孩子vector<int> right = tryRob(root->right); // 抢或不抢右孩子// 不抢当前节点,则其子节点可抢可不抢dp[0] = max(left[0], left[1]) + max(right[0], right[1]);// 抢当前节点,则其子节点绝对不能抢dp[1] = root->val + left[0] + right[0];return dp;}public:int rob(TreeNode *root){vector<int> dp = tryRob(root);return max(dp[0], dp[1]);}
};

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

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

相关文章

(贪心) LeetCode 135. 分发糖果

原题链接 一. 题目描述 n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果&#xf…

rapidjson的移植

因为rapidjson是只有头文件 使用模板来实现json 所以只需要添加头文件就行 make使用-I/路径指定头文件 解压后编写makefile 下载地址 https://github.com/Tencent/rapidjson https://github.com/Tencent/rapidjson/releases/tag/v1.1.0 下载了版本 rapidjson-1.1.0.zip 使用…

OpenCV与AI深度学习 | 基于改进YOLOv8的景区行人检测算法

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;基于改进YOLOv8的景区行人检测算法 作者&#xff1a;贵向泉&#xff0c;刘世清&#xff0c;李立等 来源&#xff1a;《计算机工程》期刊 编…

docker应用

打包传输 1.将镜像打包 #查看帮助文件 docker --help #找到save&#xff0c;可以将镜像保存为一个tar包 docker save --help #查看save使用方式 #查看现有的镜像 docker images # docker save --output centos.tar centos:latest ls ...centos.tar... 可以将tar发送给其他用户…

若依如何添加测试单元

一、引入依赖 在ruoyi-admin的pom文件中加入以下依赖&#xff0c;此处为什么不需要写版本号&#xff1f;SpringBoot版本中有对应的默认版本&#xff0c;可以防止新手小白乱写版本后引发冲突。 <dependency><groupId>org.springframework.boot</groupId><…

class_4:条件语句和逻辑运算符

mood_index int(input("对象今天的心情怎么样&#xff01;"))if mood_index > 80:print("今天可以好好happy一下了")print("O(∩_∩)O哈哈~") else:print("今天还是乖乖的吧&#xff01;否则小命不保") #BMI 体重 /&#xff08;身…

[mysql][sql]安装完mysql8跨主机不能访问解决办法

SQL语句是针对MySQL数据库的操作&#xff0c;用于更新用户信息和用户密码。 use mysql; update user set host% where userroot; ALTER USER root% identified with mysql_native_password by 密码;以下是每个命令的简要说明&#xff1a; 切换到mysql数据库: USE mysql; 这…

css实现闪烁渐变背景,@property自定义属性

自 2024 年 7 月起&#xff0c;此功能适用于最新的设备和浏览器版本。此功能可能无法在较旧的设备或浏览器中使用。 property 是 CSS 中一个相对较新的功能&#xff0c;主要用于定义自定义属性&#xff08;即 CSS 变量&#xff09;的类型、继承性以及初始值。它允许开发者更好地…

ArkTS---HAR

HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。 一、使用场景 作为二方库&#xff0c;发布到OHPM私仓&#xff0c;供公司内部其他应用使用。作为…

Linux --- 文件系统

1. 文件系统的概念 Linux 文件系统是一种用于管理、存储和组织数据的层次结构&#xff0c;用于在 Linux 操作系统中管理磁盘上的数据存储。它定义了如何在存储介质&#xff08;如硬盘、固态硬盘或 USB 闪存&#xff09;上组织文件和目录&#xff0c;以及如何读取、写入和操作这…

java整合DL645-2007与Dl645-1997

注意事项: 前导字节-一般在传输帧信息前,都要有0~4个FE不等,所以这里要注意,对于主站来说,直接发送4个FE作为前导字节即可。而从站回复,就不一定了,根据厂家不同而不同,有些没有FE的,也有4个FE的,所以对于接受程序,一定要慎重传输次序-所有的数据项都是先传低字节,…

鸿蒙HarmonyOS实战:IPC与RPC设备内进程通信

基本 IPC&#xff08;Inter-Process Communication&#xff09;与RPC&#xff08;Remote Procedure Call&#xff09;用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱动&#xff0c;用于设备内的跨进程通信&#xff0c;后者使用软总线驱动&#xff0c;用于跨设备跨进…

嵌入式八股-C++面试35题(20240824)

1. 进程和线程的基本概念是什么&#xff1f; 进程&#xff1a;进程是操作系统分配资源的基本单位&#xff0c;是一个正在运行的程序实例。每个进程都有自己的内存空间、文件句柄和其他资源。线程&#xff1a;线程是进程的一个执行单元&#xff0c;是CPU调度和分派的基本单位。…

K8S部署MySQL5.7的主从服务

mysql-slave-0是master mysql-slave-1是备份 当mysql写的时候&#xff0c;找headless service中的 mysql-slave-0.mysql57-slave-headless&#xff1b;当mysql读的时候&#xff0c;找clusterip service中的mysql57-slave-read读&#xff0c;实现读写分离。 statefulset维护两个…

linux文本分析工具grep、sed和awk打印输出文本的单双奇偶行(grep也可以打印奇偶行)以及熟悉的ssh命令却有你不知道的一些用法

一、linux文本分析工具grep、sed和awk打印输出文本的单双奇偶行&#xff08;grep也可以打印奇偶行&#xff09; 其实sed和awk要打印输出奇偶行是很容易的事情&#xff0c;不过只能使用grep来输出奇偶行的话怎么实现呢&#xff1f;今天我就想了一下&#xff0c;如果真的只能使用…

【Leetcode 1897 】重新分配字符使所有字符串都相等 —— 数组模拟哈希表

给你一个字符串数组 words&#xff08;下标 从 0 开始 计数&#xff09;。 在一步操作中&#xff0c;需先选出两个 不同 下标 i 和 j&#xff0c;其中 words[i] 是一个非空字符串&#xff0c;接着将 words[i] 中的 任一 字符移动到 words[j] 中的 任一 位置上。 如果执行任意…

Latex 插入图片或表格导致页面空白过多

如图所示&#xff1a; Latex 插入图片或表格导致页面空白过多 我们可以采用这个方式来减少空白。 \documentclass{article} \usepackage{graphicx} % 包含图形支持 \usepackage{caption} % 提供更多对caption的控制% 设置标题上方和下方的间距 \setlength{\abovecaptionskip}{…

学习记录:js算法(十二):柱状图中最大的矩形

文章目录 柱状图中最大的矩形我的思路网上思路 总结 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1:上图 输入&#xff1a;…

mac 安装Arthas

mac安装有两种方式 1.第一步安装Arthas 第一种&#xff1a; curl -L https://arthas.aliyun.com/install.sh | sh 第二种jar包形式 curl -O https://arthas.aliyun.com/arthas-boot.jar个人比较推荐第一种因为运行测试成功了 第一种安装后可能会出现一些命令不符合 需…

网安新声 | 从微软“狂躁许可”漏洞事件看安全新挑战与应对策略

网安加社区【网安新声】栏目&#xff0c;汇聚网络安全领域的权威专家与资深学者&#xff0c;紧跟当下热点安全事件、剖析前沿技术动态及政策导向&#xff0c;以专业视野和前瞻洞察&#xff0c;引领行业共同探讨并应对新挑战的策略与可行路径。 近期&#xff0c;微软披露了一个最…