第四十七天| 198.打家劫舍、213.打家劫舍II、337.打家劫舍III

Leetcode 198.打家劫舍

题目链接:198 打家劫舍

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

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

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

思考:动态规划。难点:当前房屋偷与不偷取决于 前一个房屋和前两个房屋是否被偷。

  • 确定dp数组(dp table)以及下标的含义

dp[i]:下标i(包括i)以内的房屋,偷窃的最高金额。

  • 确定递推公式

决定dp[i]的因素就是第i房间偷还是不偷。

如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。

如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房。

并且dp[i]取最大值,则dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);

  • dp数组如何初始化

从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]

从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);

  • 确定遍历顺序

由于dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历。

  • 例推导dp数组

举例:[2,7,9,3,1]。dp状态如图:

代码:

class Solution {
public:int rob(vector<int>& nums) {if (nums.size() == 0)   return 0;           //情况一:数组长度为0if (nums.size() == 1)   return nums[0];     //情况二:数组长度为1vector<int> dp(nums.size());     //下标i(包括i)以内的房屋,偷窃的最高金额dp[0] = nums[0];dp[1] = max(nums[0], nums[1]);for (int i = 2; i < nums.size(); i++)      //遍历房间dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);return dp[nums.size() - 1]; }
};

Leetcode 213.打家劫舍II

题目链接:213 打家劫舍II

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

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

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

思考:动态规划。本题和上题的唯一区别就是成环。

对于一个数组,成环的话主要有如下三种情况:

  • 情况一:考虑不包含首尾元素

  • 情况二:考虑包含首元素,不包含尾元素

  • 情况三:考虑包含尾元素,不包含首元素

情况二 和 情况三 都包含了情况一,所以只需要用上题的处理逻辑考虑情况二和情况三即可,返回最大值。 

代码:

class Solution {
public://打家劫舍的逻辑int robRange(const vector<int>& nums, int start, int end) {if (start == end)   return nums[start];     //打劫区间为1vector<int> dp(nums.size());     //下标i(包括i)以内的房屋,偷窃的最高金额dp[start] = nums[start];dp[start + 1] = max(nums[start], nums[start + 1]);for (int i = start + 2; i <= end; i++)      //遍历房间dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);return dp[end]; }int rob(vector<int>& nums) {if (nums.size() == 0)   return 0;                       //情况一:数组长度为0if (nums.size() == 1)   return nums[0];                 //情况二:数组长度为1int result1 = robRange(nums, 0, nums.size() - 2);       //情况三:不考虑最后的房间int result2 = robRange(nums, 1, nums.size() - 1);       //情况四:不考虑首个房间return max(result1, result2);}
};

Leetcode  337.打家劫舍III

题目链接:337 打家劫舍III

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

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

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

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

思考一:递归法 + 暴力法。终止条件:如果当前节点为空,则返回0。如果当前节点为叶子节点,则不用处理直接返回此节点值即可。

 单层递归逻辑:考虑两种情况。情况一:偷根节点,由于相邻节点不能都偷,因此递归处理左右孩子节点的孩子节点,并累加值、记录到val1。情况二:不偷根节点,则递归处理左右孩子节点,并累加值,记录到val2。取val1和val2的较大值返回。

按以上方式处理会超时,因此要添加备忘录,用于记录每个子树盗取的最高金额。当然单层递归逻辑要将上面的最大值记录下。此外终止条件要加上如果备忘录记录过,则返回记录数据。

代码:

class Solution {
public:unordered_map<TreeNode*, int> memo;     //备忘录 记录每个子树盗取的最高金额int rob(TreeNode* root) {if (!root)  return 0;       //空节点返回0if (!root->left && !root->right)    return root->val;       //叶子节点返回节点值if (memo[root]) return memo[root];      //备忘录中添加过则直接返回数据//偷根节点int val1 = root->val;if (root->left) val1 += rob(root->left->left) + rob(root->left->right);      //不偷左孩子节点if (root->right) val1 += rob(root->right->left) + rob(root->right->right);      //不偷右孩子节点//不偷根节点int val2 = rob(root->left) + rob(root->right);memo[root] = max(val1, val2);       //记录数据return memo[root];}
};

思考二:递归法 + 动态规划。动态规划其实就是使用状态转移容器来记录状态的变化,这里可以使用一个长度为2的数组,记录当前节点偷与不偷所得到的的最大金钱。

下面以递归三部曲为框架,其中融合动规五部曲的内容来进行讲解思路

  • 确定递归函数的参数和返回值

这里我们要求一个节点 偷与不偷的两个状态所得到的金钱,那么返回值就是一个长度为2的数组。其实就是dp数组,dp[0]表示不偷当前节点的情况,dp[1]表示偷当前节点的情况。这些dp数组保存在系统栈中,递归返回时使用。

  • 确定终止条件

在遍历的过程中,如果遇到空节点的话,无论偷还是不偷都是0。这也是动态规划的初始化过程。

  • 确定遍历顺序

由于处理完节点后, 栈中保存的dp数组数据在返回时才使用,因此使用后序遍历处理二叉树。

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

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

  • 确定单层递归的逻辑

如果是偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; 

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

最后当前节点的状态就是{val2, val1}; {不偷当前节点得到的最高金额,偷当前节点得到的最高金额}

这也是动态规划确定递推公式的过程。

  • 举例推导dp数组

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

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

代码:

class Solution {
public:// 长度为2的数组,0:不偷,1:偷vector<int> robTree(TreeNode* cur) {if (!cur)   return vector<int>{0, 0};vector<int> left = robTree(cur->left);          //左vector<int> right = robTree(cur->right);        //右int val1 = cur->val + left[0] + right[0];                       //偷当前节点int val2 = max(left[0], left[1]) + max(right[0], right[1]);    //不偷当前节点return vector<int>{val2, val1};}int rob(TreeNode* root) {vector<int> result = robTree(root);return max(result[0], result[1]);}
};

自我总结:

了解树形动态规划。感受递归+动态规划的设计逻辑:利用系统栈传递dp数组,相当于刷新dp数组

关于为什么树形结构,进行动态规划处理比进行暴力法处理节省时间以及为什么不会爆栈,先留下疑问,等理解清晰回头写思路。

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

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

相关文章

12-Linux部署Zookeeper集群

Linux部署Zookeeper集群 简介 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布式同步、组服务等。…

jmeter 压测数据库

当前版本&#xff1a; jmeter 5.6.3mysql 5.7.39 简介 JMeter 是一个开源的 Java 应用程序&#xff0c;主要用于进行性能测试和负载测试。它支持多种协议&#xff0c;包括但不限于 HTTP、HTTPS、FTP、JDBC 以及各种 Web Services。对于数据库的压力测试可以使用 JDBC 协议与数…

excel导入标准化

excel导入较导出还是复杂一些&#xff0c;一般分为三个步骤.市面上低代码平台可以将常用的操作固化&#xff0c;并且形成收益&#xff0c;这也是挺好的。我将我的一些总结分享到网上也是我自己乐意的。毕竟尊重技术的还是搞技术的自身&#xff0c;一般企业老板并不太关心技术代…

Spring中@import注解终极揭秘!

技术概念 它能干啥 Import注解在Spring框架中主要用于解决模块化和配置管理方面的技术问题&#xff0c;它可以帮助开发者实现以下几个目标&#xff1a; 模块化配置&#xff1a;在大型项目中&#xff0c;通常需要将配置信息分散到多个配置类中&#xff0c;以便更好地组织和管…

FPGA-DDS原理及实现

DDS(Direct Digital Synthesizer)即数字合成器,是一种新型的频率合成技术,具有相对带宽大,频率转换时间短、分辨率高和相位连续性好等优点。较容易实现频率、相位以及幅度的数控调制,广泛应用于通信领域。 相位累加器是由N位加法器与N位寄存器构成,每个时钟周期的上升沿,加法器…

数据中心制冷系统设计与发展

数据中心概要与传统建筑空间相比&#xff0c;数据中心散热密度大&#xff0c;单位面积散热量可达传统办公区域的40倍以上&#xff0c;且越来越呈现集中化、大型化的趋势&#xff1b;同时&#xff0c;设备的安全性需求提高了对内部空调温湿度和洁净度的要求&#xff0c;数据中心…

【Qt】Qwidget的常见属性

目录 一、Qwidget核心属性 二、enable属性 三、geometry属性 四、 WindowFrame的影响 五、windowTitle属性 六、windowIcon属性 七、qrc文件管理资源 八、windowOpacity属性 九、cursor属性 十、font属性 十一、toolTip属性 十二、focusPolicy属性 十三、styleShe…

STM32FreeRTOS-事件组1(STM32Cube高效开发教程)

文章目录 一、事件组的原理和功能1、事件组与队列信号量特点2、事件组存储结构3、事件组运行原理 二、事件组部分函数1、xEventGroupCreate()创建事件组函数2、xEventGroupSetBits&#xff08;&#xff09;事件组置位函数3、xEventGroupSetBitsFromISR&#xff08;&#xff09;…

Sychronized和ReentrantLock锁 面试题

Sychronized和ReentrantLock锁 面试题 前言1、Java死锁如何避免&#xff1f;2、公平锁和⾮公平锁的底层实现&#xff1f;3、ReentrantLock中tryLock()和lock()⽅法的区别&#xff1f;4、Sychronized的偏向锁、轻量级锁、重量级锁&#xff1f;5、谈谈你对AQS的理解&#xff0c;A…

SQL技巧笔记(一):连续3人的连号问题—— LeetCode601.体育馆的人流量

SQL 技巧笔记 前言&#xff1a;我发现大数据招聘岗位上的应聘流程都是需要先进行笔试&#xff0c;其中占比很大的部分是SQL题目&#xff0c;经过一段时间的学习之后&#xff0c;今天开了一个力扣年会员&#xff0c;我觉得我很有必要去多练习笔试题目&#xff0c;这些题目是有技…

代码随想录算法训练营第三十七天 | LeeCode 738. 单调递增的数字

题目链接&#xff1a;738. 单调递增的数字 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int monotoneIncreasingDigits(int N) {string strNum to_string(N);// flag用来标记赋值9从哪里开始// 设置为这个默认值&#xff0c;为了防止第二个for循环在fla…

Linux - 进程概念

1、冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系&#xff1b; 截至目前&#xff0c;我们所认识的计算机&#xff0c;都是有一个个的硬件组件组成&#xff1a; 输入单元&#xff1a;…

【JavaEE】_Spring MVC项目使用数组与集合传参

目录 1. 使用数组传参 1.2 传递单个参数 1.3 传递多个名称相同的参数 1.3.1 关于urlencode 2. 使用集合传参 1. 使用数组传参 创建一个Spring MVC项目&#xff0c;其中 .java文件内容如下&#xff1a; package com.example.demo.controller;import com.example.demo.Per…

2.Zookeeper集成springboot操作节点,事件监听,分布式锁实现

1.Springboot项目中添加zookeeper 已经对应的客户端依赖 &#xff0c;pom.xml文件如下 <!-- Zookeeper组件 --><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.9.1</version…

【C++】6-8 评委打分 分数 10

6-8 评委打分 分数 10 全屏浏览 切换布局 作者 刘利 单位 惠州学院 某诗歌朗诵比赛&#xff0c;有n位评委给参赛者打分&#xff0c;计算总分时要去除最高分和对低分。 要求&#xff1a;编写名为cmax和cmin的函数分别返回最高分的和最低分元素的引用&#xff0c;带有2个形参…

leetcode面试经典算法题——1

链接&#xff1a;https://leetcode.cn/studyplan/top-interview-150/ 392. 判断子序列 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串…

292.【华为OD机试】跳马问题(广度优先搜索(BFS)JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目二.解题思路三.题解代码Python题解代码JAVA题解…

分布式事务(SeataServer)

SeataServer搭建 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Servi…

JavaScript 闭包 作用域

闭包 JavaScript 中的闭包是相当重要的概念并且与作用域相关知识的指向密切相关 JavaScript 中的作用域是什么意思?闭包会在哪些场景中使用?通过定时器循环输出自增的数字通过 JS 的代码如何实现? 闭包概念一 闭包是指有权访问另外一个函数作用域中的变量的函数。 闭包…

自我对比: 通过不一致的解决视角更好地进行反思

一、写作动机&#xff1a; LLM 在自我评价时往往过于自信或随意性较大&#xff0c;提供的反馈固执或不一致&#xff0c;从而导致反思效果不佳。为了解决这个问题&#xff0c;作者提倡 "自我对比"&#xff1a; 它可以根据要求探索不同的解决角度&#xff0c;对比差异…