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.rhkb.cn/news/408147.html

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

相关文章

贪心+栈。。

前言&#xff1a;这个题目一开始我没想通的就是如果s当前的一个字符或者之后的一个字符和当前t的尾巴是一样的&#xff0c;那么优先选哪一个&#xff0c;其实这个就要优先选t的 class Solution { public:string robotWithString(string s) {string ans;int cnt[26]{}, min 0; …

<C++> 二叉搜索树

目录 二叉搜索树 1. 概念 2. 二叉搜索树操作 2.1 基础结构 2.2 非递归版 1. 查找 2. 插入 3. 删除 2.3 递归版 1. 查找 2. 插入 3. 删除 2.4 拷贝构造函数 2.5 赋值运算符重载 2.6 析构函数 2.7 完整代码 3. 二叉搜索树的应用 4. 二叉搜索树的性能 二叉搜索树 1. 概念 二叉搜索…

SpringBoot+Vue3整合minio,实现分布式文件存储

文章目录 几种常用的文件存储安装和使用minioSpringBoot整合minio 基本所有的软件项目都会需要文件存储功能&#xff0c;图片、视频存储。 几种常用的文件存储 经常用的几种方案&#xff0c;直接存在本地文件夹&#xff0c;开发一个简单的系统当然没有问题。随机系统所需的资源…

微服务多个模块启动,端口被占用,yml配置文件读不到

刚刚提交到gitee自己的仓库&#xff0c;拉下来还是报错&#xff0c;然后看到一个解决方法&#xff1a; <build><resources><resource><directory>src/main/java</directory><includes><include>**/*.yml</include><includ…

推荐一个java低代码开发平台-橙单

文章目录 前言一、项目介绍二、技术选型三、项目特点四、基础功能介绍五、源码下载六、官方文档总结 前言 大家好&#xff0c;今天为大家推荐一个开箱即用&#xff0c;快速开发的低代码平台。项目采用 Boot3 Flowable7 Sa-Token Vue3技术栈。 一、项目介绍 橙单中台化低代…

Datawhale AI 夏令营(第五期) 李宏毅苹果书 Task 1 《深度学习详解(入门)》- 1.1 通过案例了解机器学习

预测本频道观看人数&#xff08;上&#xff09; - 机器学习基本概念简介_哔哩哔哩_bilibili 1 隐藏任务&#xff1a;找出本篇中形如回归&#xff08;regression&#xff09;加粗字体的术语&#xff0c;并用自己的话进行解释&#xff0c;列成表格 术语解释机器学习&#xff08;…

服务器数据恢复—重建RAID失败导致数据丢失的数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌服务器中有一组由4块SAS磁盘做的RAID5磁盘阵列。该服务器操作系统为windows server&#xff0c;运行了一个单节点Oracle&#xff0c;数据存储为文件系统&#xff0c;无归档。该oracle数据库的数据量不大&#xff0c;oracle数据库内只有一…

WPF——动态排名图表实现

开发环境 VS2022 .NET 8.0 MVVM Toolkit 8.2.2 需求 开发中需要实现按照成绩动态指名&#xff0c;以展示当前的竞赛成绩的一个实时情况及变化。 即如下效果&#xff1a; 需求分析 按照接收到的信息&#xff0c;就是要将获取到的集合排序&#xff0c;并且要将排序前后的变…

【AI绘画】Midjourney前置指令/settings设置详解

文章目录 &#x1f4af;Midjourney前置指令/settings设置详解&#x1f4af;Use the default model&#xff08;AI绘画所使用的大模型&#xff09;Midjourney Model&#xff08;Midjourney 模型&#xff09;Niji Model&#xff08;Niji模型&#xff09; &#x1f4af;Midjourney…

外网爆火的LLM应用手册来了!内行人都在学的大模型黑书,豆瓣评分高达9.9!!!

Transformer模型介绍 Transformer 是工业化、同质化的后深度学习模型&#xff0c;其设计目标是能够在高性能计算机(超级计算机)上以并行方式进行计算。通过同质化&#xff0c;一个Transformer 模型可以执行各种任务&#xff0c;而不需要微调。Transformer 使用数十亿参数在数…

【Java数据结构】---二叉树OJ

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录 相同的树另一颗树的子树翻…

ES 模糊查询 wildcard 的替代方案探索

一、Wildcard 概述 Wildcard 是一种支持通配符的模糊检索方式。在 Elasticsearch 中&#xff0c;它使用星号 * 代表零个或多个字符&#xff0c;问号 ? 代表单个字符。 其使用方式多样&#xff0c;例如可以通过 {"wildcard": {"field_name": "value&…

docker-compose示例:nacos单机部署

前面咱们完成了docker基本环境搭建&#xff0c;下面就趁热打铁来练习下nacos的单机部署。 参考官方文档&#xff1a;Nacos Docker 快速开始。考虑到官方搭建教程过于精炼&#xff0c;笔者把搭建过程分享给大家。 文章目录 下载最新部署源码解决网络导致的sql文件下不下来docke…

unity AssetBundle 使用_什么是AssetBundle_导入必要的插件_创建AssetBundles_AB包资源下载_大文件下载

一、什么是AssetBundle&#xff1f; 定义AssetBundle。 AssetBundle 是一个存档文件&#xff0c;包含可在运行时由 Unity 加载的特定于平台的非代码资源&#xff08;比如模型、纹理、预制件、音频剪辑甚至整个场景&#xff09;。AssetBundle 可以表示彼此之间的依赖关系&…

防范小程序隐私合规风险,筑牢用户信任防线

随着国内APP软件生态的成熟&#xff0c;依托于头部APP的小程序逐渐成为零售、娱乐、出行等行业必选的获客渠道之一。较低的开发成本和成熟的用户营销功能&#xff0c;令小程序的数量在过去几年呈指数级增长。截止2023年&#xff0c;头部APP内集成的小程序总量已超千万。然而&am…

OpenCV(开源计算机视觉库)

OpenCV&#xff08;开源计算机视觉库&#xff09;是一个专注于实时计算机视觉的全面库&#xff0c;包含了丰富的工具和功能。以下是 OpenCV 中一些关键知识点的详细列表&#xff1a; 核心功能 基本结构&#xff1a;Mat、Scalar、Point、Size、Rect 等。 图像 I/O&#xff1a;读…

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

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

数据结构【链试结构二叉树】

&#x1f31f;个人主页&#xff1a;落叶 目录 ​编辑 实现链式结构⼆叉树 前中后序遍历&#xff1a; 遍历规则 代码实现 前序遍历&#xff1a; 中序遍历&#xff1a; 后序遍历&#xff1a; 图解遍历&#xff1a; 函数递归栈帧图&#xff1a; 结点个数以及高度等 【⼆…

Oracle归档日志满了,导致程序打不开,如何解决。

加油&#xff0c;新时代打工人&#xff01; 归档日志错误&#xff0c;登录不上&#xff0c;只能用system 角色登录&#xff0c; 错误提示 oracle 错误257 archiver error connect internal only until freed 解决cmd进入rman RMAN&#xff08;Recovery Manager&#xff09;是一…

数学基础(六)

一、分布 正态分布 二项式分布 均匀分布 卡方分布 二、核函数 核函数的目的&#xff1a; 将低维数据转换为高维数据 线性核函数&#xff1a; Linear核函数对数据不做任何变换 当特征已经比较丰富了&#xff0c;样本数据量巨大&#xff0c;需要进行实时得出结果时进行使用…