动态规划篇-代码随想录算法训练营第三十七天| 打家劫舍Ⅰ,打家劫舍Ⅱ,打家劫舍Ⅲ

本文主要是介绍动态规划篇-代码随想录算法训练营第三十七天| 打家劫舍Ⅰ,打家劫舍Ⅱ,打家劫舍Ⅲ,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

打家劫舍Ⅰ

题目链接:. - 力扣(LeetCode)

讲解视频:

动态规划,偷不偷这个房间呢?| LeetCode:198.打家劫舍

题目描述:

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

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

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。

解题思路:

1、状态表示:
f[i]:偷到i位置时,偷nums[i],此时的最大金额

g[i]:偷到i位置时,不偷nums[i],此时的最大金额

2、状态转移方程:

f[i] = g[i-1] + nums[i];

g[i] = max(g[i-1],f[i-1]);

3、初始化:

f[0] = nums[0], g[0] = 0

4、遍历顺序:

从左往右

5、返回值:

返回max(f[n-1], g[n-1])

代码:

class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();vector<int> f(n);auto g = f;f[0] = nums[0];for(int i = 1; i < n; i++){f[i] = g[i-1] + nums[i];g[i] = max(g[i-1],f[i-1]);}return max(f[n-1],g[n-1]);}
};

打家劫舍Ⅱ

题目链接:. - 力扣(LeetCode)

讲解视频:

动态规划,房间连成环了那还偷不偷呢?| LeetCode:213.打家劫舍II

题目描述:

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

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

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

解题思路:

这⼀个问题是「打家劫舍I」问题的变形。上⼀个问题是⼀个「单排」的模式,这⼀个问题是⼀个「环形」的模式,也就是⾸尾是相连的。但是我们可以将「环形」问题转化为「两个单排」问题:

  1. 偷第⼀个房屋时的最⼤⾦额 x ,此时不能偷最后⼀个房⼦,因此就是偷 [0, n - 2] 区间的房⼦;
  2. 不偷第⼀个房屋时的最⼤⾦额 y ,此时可以偷最后⼀个房⼦,因此就是偷 [1, n - 1] 区间的房⼦;

两种情况下的「最⼤值」,就是最终的结果。因此,问题就转化成求「两次单排结果的最⼤值」。

代码:

class Solution {
public:int  cirrob(vector<int>& nums, int left, int right){if(left > right) return 0;int n = nums.size();vector<int> f(n);auto g = f;f[left] = nums[left];for(int i = left+1; i <= right; i++){f[i] = g[i-1]+nums[i];g[i] = max(f[i-1],g[i-1]);}return max(f[right],g[right]);}int rob(vector<int>& nums) {int n = nums.size();return max(nums[0]+cirrob(nums,2,n-2),cirrob(nums,1,n-1));}
};

打家劫舍Ⅲ

题目链接:. - 力扣(LeetCode)

讲解视频:

动态规划,房间连成树了,偷不偷呢?| LeetCode:337.打家劫舍3

题目描述:

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

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

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

示例 1:

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

解题思路:

结合递归三部曲的动态规划五部曲

1、状态表示(确定递归函数的参数和返回值)

这里我们要求一个节点偷与不偷的两个状态所得到的金钱,那么

返回值就是一个长度为2的数组,参数为当前节点。代码如下:

vector<int> robTree(TreeNode* cur) {

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

所以dp数组(dp table)以及下标的含义:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。

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

2、初始化(确定终止条件)

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

if (cur == NULL) return vector<int>{0, 0};

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

3、确定遍历顺序

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

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

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

代码如下:

// 下标0:不偷,下标1:偷
vector<int> left = robTree(cur->left); // 左
vector<int> right = robTree(cur->right); // 右
// 中

4、确定单层递归的逻辑

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

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

最后当前节点的状态就是{val2, val1}; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}

代码如下:

vector<int> left = robTree(cur->left); // 左
vector<int> right = robTree(cur->right); // 右// 偷cur
int val1 = cur->val + left[0] + right[0];
// 不偷cur
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
return {val2, val1};

5、举例推导dp数组

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

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

代码:

class Solution {
public:vector<int> treeRob(TreeNode* cur){if(cur == nullptr) return {0,0};vector<int> left = treeRob(cur->left);vector<int> right = treeRob(cur->right);int f = cur->val + left[0] + right[0];//选int g = max(left[0],left[1]) + max(right[0],right[1]);//不选return {g,f};}int rob(TreeNode* root) {vector<int> result = treeRob(root);return max(result[0],result[1]);}
};

这篇关于动态规划篇-代码随想录算法训练营第三十七天| 打家劫舍Ⅰ,打家劫舍Ⅱ,打家劫舍Ⅲ的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

IDEA实现回退提交的git代码(四种常见场景)

《IDEA实现回退提交的git代码(四种常见场景)》:本文主要介绍IDEA实现回退提交的git代码(四种常见场景),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.已提交commit,还未push到远端(Undo Commit)2.已提交commit并push到

Kotlin Compose Button 实现长按监听并实现动画效果(完整代码)

《KotlinComposeButton实现长按监听并实现动画效果(完整代码)》想要实现长按按钮开始录音,松开发送的功能,因此为了实现这些功能就需要自己写一个Button来解决问题,下面小编给大... 目录Button 实现原理1. Surface 的作用(关键)2. InteractionSource3.

golang实现动态路由的项目实践

《golang实现动态路由的项目实践》本文主要介绍了golang实现动态路由项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习... 目录一、动态路由1.结构体(数据库的定义)2.预加载preload3.添加关联的方法一、动态路由1

使用Java实现Navicat密码的加密与解密的代码解析

《使用Java实现Navicat密码的加密与解密的代码解析》:本文主要介绍使用Java实现Navicat密码的加密与解密,通过本文,我们了解了如何利用Java语言实现对Navicat保存的数据库密... 目录一、背景介绍二、环境准备三、代码解析四、核心代码展示五、总结在日常开发过程中,我们有时需要处理各种软

Java List排序实例代码详解

《JavaList排序实例代码详解》:本文主要介绍JavaList排序的相关资料,Java排序方法包括自然排序、自定义排序、Lambda简化及多条件排序,实现灵活且代码简洁,文中通过代码介绍的... 目录一、自然排序二、自定义排序规则三、使用 Lambda 表达式简化 Comparator四、多条件排序五、

Java 压缩包解压实现代码

《Java压缩包解压实现代码》Java标准库(JavaSE)提供了对ZIP格式的原生支持,通过java.util.zip包中的类来实现压缩和解压功能,本文将重点介绍如何使用Java来解压ZIP或RA... 目录一、解压压缩包1.zip解压代码实现:2.rar解压代码实现:3.调用解压方法:二、注意事项三、总

Linux实现简易版Shell的代码详解

《Linux实现简易版Shell的代码详解》本篇文章,我们将一起踏上一段有趣的旅程,仿照CentOS–Bash的工作流程,实现一个功能虽然简单,但足以让你深刻理解Shell工作原理的迷你Sh... 目录一、程序流程分析二、代码实现1. 打印命令行提示符2. 获取用户输入的命令行3. 命令行解析4. 执行命令

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

uniapp小程序中实现无缝衔接滚动效果代码示例

《uniapp小程序中实现无缝衔接滚动效果代码示例》:本文主要介绍uniapp小程序中实现无缝衔接滚动效果的相关资料,该方法可以实现滚动内容中字的不同的颜色更改,并且可以根据需要进行艺术化更改和自... 组件滚动通知只能实现简单的滚动效果,不能实现滚动内容中的字进行不同颜色的更改,下面实现一个无缝衔接的滚动

利用Python实现可回滚方案的示例代码

《利用Python实现可回滚方案的示例代码》很多项目翻车不是因为不会做,而是走错了方向却没法回头,技术选型失败的风险我们都清楚,但真正能提前规划“回滚方案”的人不多,本文从实际项目出发,教你如何用Py... 目录描述题解答案(核心思路)题解代码分析第一步:抽象缓存接口第二步:实现两个版本第三步:根据 Fea