代码随想录算法训练营DAY47|C++动态规划Part8|198.打家劫舍、213.打家劫舍II、198.打家劫舍III

文章目录

  • 198.打家劫舍
    • 思路
    • CPP代码
  • 213.打家劫舍II
    • 解决环的问题
    • 思路
    • 总结
    • CPP代码
  • 198.打家劫舍III
    • 思路
      • 递归三部曲——确定参数和返回值
      • 递归三部曲——确定终止条件
      • 递归三部曲——确定单层遍历的逻辑
    • 打印dp数组
    • CPP代码

198.打家劫舍

力扣题目链接

文章讲解:198.打家劫舍

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

这里把题目再抽象一下:

偷钱币的规则——相邻房间不能偷,然后随便怎么偷都行

本题最大的难点就是,虽说相邻房间不能偷,但是剩下的都可以偷,甚至隔好几个房间再偷。那么当前这个房间到底偷不偷呢?这是最关键的点

其实我们当前房间偷不偷本质上依赖于我们前一个房间和前两个房间偷与不偷的状态,如果前一个房间被偷了,那本房间绝对不能偷,如果偷了前两个房间,那么这个房间可以偷也可以不偷,所以当前房间偷不偷和前一个和前两个房间的状态是由关系的,这个关系也就是我们的递推关系。

思路

  • dp数组的含义

我们考虑数组下标i(包含下标i),它所偷的最大的钱币就是dp[i],最终的结果在dp[nums.size() - 1]

  • 递推公式

对于房间i,无非就是偷或者不偷,

如果我们明确要偷i,那么i前面的那个房间是一定不考虑的,所以最多只能考虑到dp[i-2],其中的dp[i-2]表示在i-2(包含)及之前的所有房间所能偷的最大金币数量再加上i房间的金币数量,所以递推公式

d p [ i − 2 ] + n u m s [ i ] dp[i-2]+nums[i] dp[i2]+nums[i]

如果我们明确不偷i,此时我们就可以考虑i-1以及i-1之前的所有房间所能偷的最大金币数量,所以递推公式

d p [ i − 1 ] dp[i-1] dp[i1]

综上所示,递推公式为

dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
  • 初始化

从递归公式可以看出来dp[i]是由dp[i-2]dp[i-1]推导出来的,所以递推公式的基础就是dp[0]dp[1]

很明显dp[0]=nums[0] ,至于dp[1]=max(nums[0], nums[1])

非零和非一下标初始化成0即可

  • 遍历顺序

    这里遍历顺序就很简单了,没什么讲究

for (int i = 2; i < nums.size(); i++)
  • 打印

CPP代码

class Solution {
public:int rob(vector<int>& nums) {if (nums.size() == 0) return 0;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

力扣题目链接

文章讲解:213.打家劫舍II

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

本题与198.打家劫舍有什么区别呢?唯一的区看别就是本题把线性数组首尾相连连成环了,这会导致规则有什么样的变化呢?

  • 相邻房间不偷,这是肯定的
  • 首尾元素只能选一个偷。因为本题首尾相连了!

一旦连成环就很容易懵,本题还有起点和终点吗?起点和终点应该如何选呢?

解决环的问题

如果连成环的话,首尾元素如果要选的话只能选择一个,要不选首元素要不尾元素,要不都不选。其实本质上我们可以划分成三个情况,这里拿[1, 6, 1, 9, 1]举例

  • 情况一:不考虑首元素也不考虑尾元素,只考虑中间的[6, 1, 9]数组范围

此时,该数组变成了一个线性数组,所以我们其实可以用线性数组的方式来解决本题,一下就变成了198.打家劫舍

  • 情况二:考虑首元素,不考虑尾元素了,即考虑[1, 6, 1, 9]数组范围。所以本题仍然能变成一个线性数组
  • 情况三:考虑尾元素,不考虑首元素了,即考虑[6, 1, 9, 1]数组范围。所以仍然变成一个线性数组解体

本题中的考虑首位元素并不代表我一定会选择首元素或尾元素,具体的选择是由递推公式决定的

现在再来分析一下这三种情况,从元素情况其实就可以看出,其实情况二和情况三已经包含了情况一的中间元素了。

所以综上所述:我们只要求情况二和情况三的最优解,然后取一个最大值就行

思路

上文中其实已经阐明了大部分的思路,现在我们的情况二和情况三已经是线性数组了,我们只需要把情况二[1, 6, 1, 9]传到我们的198.打家劫舍函数中,然后把情况三传到198.打家劫舍函数,取一个max

总结

我们竟然把一个环形数组的题解耦成了一个线性数组!卡哥牛逼!

CPP代码

// 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了
class Solution {
public:int rob(vector<int>& nums) {if (nums.size() == 0) return 0;if (nums.size() == 1) return nums[0];int result1 = robRange(nums, 0, nums.size() - 2); // 情况二int result2 = robRange(nums, 1, nums.size() - 1); // 情况三return max(result1, result2);}// 198.打家劫舍的逻辑int robRange(vector<int>& nums, int start, int end) {if (end == start) return nums[start];vector<int> dp(nums.size());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 - 2] + nums[i], dp[i - 1]);}return dp[end];}
};

198.打家劫舍III

力扣题目链接

文章讲解:198.打家劫舍III

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

状态:入门级树形dp,需要在二叉树中进行一个状态转移。只能说卡哥太牛逼了,这题的讲解真的是绝

基本规则和198.打家劫舍一样,只不过我们本题中数据结构变成了一个二叉树。

本题如果对二叉树不够了解,对二叉树的遍历不够娴熟的话,基本就是寄。

所以本题的思路也应该是以递归三部曲为主旋律,穿插动态规划五部曲的过程

思路

本文以root=[3, 4, 5, 1, 3, 5, 1]为例,

本题既然是遍历树形结构,那么肯定逃不开递归,所以本质是在单层递归逻辑中采用了动态规划的相关思想。

递归三部曲——确定参数和返回值

参数可能还懵懵懂懂能猜到,肯定是传入根结点!

那返回值是什么呢?dp数组是什么呢?如何融入树形遍历中呢?

首先最主要的问题肯定是解决dp数组的含义,在树形结构的递归遍历中,有一个最基本的思想,也就是不同的遍历方法可以去处理不同的逻辑。本题中,如果我们想偷当前节点,那么相邻结点也就是左右孩子节点肯定不能偷;如果偷左右孩子结点,那么当前节点就不能偷。所以肯定是用后序遍历,因为我们的当前结点的状态只要根据左右孩子的状态来的。

总而言之,这种情况最好的方法就是,用dp数组去定义每个结点的状态。

  • 确定dp数组的含义

每个节点只有两个状态,也就是说节点偷还是不偷,所以用一个长度为2的一维dp数组**dp[0]表示不偷当前节点情况的最大价值,dp[1]表示偷**当前结点情况的最大价值。总而言之就是每一个结点,都存在这样一个dp数组状态

  • dp数组的递推公式

本题使用的是后序遍历的方式,由底向上推,最后就有根结点是偷还是不偷的两个状态,那我们最终的结果就是根结点偷还是不偷的两个状态我们取一个最大值,那这个就是我们最后的结果。

那么我们定义当前节点左右孩子的dp数组状态为leftdprightdp

再回到当前节点偷与不偷,当然选一个dp数组的最大值,所要要定义一个value1表示偷当前结点我们取得的最大钱币,那么如果我们偷了当前结点,那么本节点的左右孩子就一定不能偷了,那么我们要计算不偷左孩子能得到的最大钱币加上不偷右孩子能得到的最大钱币;

int value1 = cur->val + leftdp[0] + rightdp[0]

好了,现在我们必须要考虑偷左右孩子的情况了,如果偷左右任意一个孩子的话,那我们就不能偷当前结点。 再一个,左孩子偷不偷不是由我们去决定的,而是由leftdp[0]leftdp[1]谁大就选哪个;右孩子偷还是不偷也是一样的道理,并且左右孩子是可以同时偷的,因为他们不算相邻结点。所以以代码如下:

int value2 = max(leftdp[0], leftdp[1]) + max(rightdp[0], rightdp[1]);

此时,我得到了两个状态value1value2,value1是我偷当前结点的结果,value2是我不偷当前结点的结果,那么当前状态的偷和不偷就出来了,所以我们返回什么呢?

// 返回当前节点的dp数组
return {value2, value1} //{不偷,偷}

递归三部曲——确定终止条件

if (cur == NULL) return vector<int> (0, 0); //本质上是返回空间的dp数组的状态

如果当前节点是NULL的话,也就是我们此时遍历到了空节点,我们此时偷与不偷其实都是0,所以要返回一个元素都是0的dp数组。

递归三部曲——确定单层遍历的逻辑

我们已经在递归三部曲——确定参数和返回值:确定dp数组的递推公式中讲完了。

打印dp数组

以下图为例:

各个节点的dp数组为:(两列分别为dp[0]、dp[1],行数为后序遍历的顺序)

CPP代码

class Solution {
public:int rob(TreeNode* root) {vector<int> result = robTree(root);return max(result[0], result[1]);}//长度为2的数组, 0:不偷 1:偷vector<int> robTree (TreeNode* cur) {if (cur == NULL) return vector<int>{0, 0};vector<int> leftdp = robTree(cur->left);vector<int> rightdp = robTree(cur->right);//偷cur,那么就不能偷左右结点。int val0 = cur->val + leftdp[0] + rightdp[0];//不偷cur,那么可以偷也可以不偷左右结点,则取较大的情况int val1 = max(leftdp[0], left[1]) + max(rightdp[0], rightdp[1]);return {val1, val0};}
}

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

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

相关文章

SVN--基本原理与使用(超详细)

目录 一、SVN概述二、SVN服务端软件安装三、SVN服务端配置四、SVN客户端软件安装与使用五、SVN三大指令六、SVN图标集与忽略功能6.1 图标集6.2 忽略功能 七、SVN版本回退八、SVN版本冲突九、SVN配置多仓库与权限控制9.1 配置多仓库9.2 权限控制 十、服务配置与管理十一、模拟真…

Linux 第十七章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

刷机维修进阶教程-----VIVO机型 修复基带 更改参数 实例步骤操作解析

写到前面: 任何参数修改和刷写分区都有风险。严重会导致无基带 无信号等故障。操作前都需要谨慎,养成备份关键数据分区的习惯。以便出现问题可以恢复。不管是修复基带分区还是更改相关参数,在操作前都有可以恢复原分区数据与刷机救砖的能力在操作。今天以vivo机型来做演示。…

bun 换源 国内阿里源 npmmirror 加速下载

Github https://github.com/oven-sh/bun 版本号 bun 1.1.5 windows 安装 bun 如果本机有 nodejs 环境, 可以 npm install -g bun 安装 ( 官方把 exe 已经传到了 npm 仓库, 走的国内 npm 镜像, 下载速度会很快) 没有 nodejs, 可以用 powershell 脚本安装 具体操作 全局 …

力扣刷题 63.不同路径 II

题干 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上角到…

Pytorch GPU版本安装

一、背景 记录一下安装Pytorch GPU版本过程。 由于手残&#xff0c;卸载了电脑上的显卡驱动&#xff0c;现在我连显卡类型是啥都不知道了。 总体思路&#xff1a;安装显卡驱动->安装cuda->安装pytorch库 二、安装显卡驱动 2.1 查看本地显卡型号 通过「DirectX 诊断工具…

Leetcode 145:二叉树的后序遍历(迭代法)

给你一棵二叉树的根节点 root &#xff0c;返回其节点值的 后序遍历 。 思路&#xff1a; 迭代法的思路是&#xff0c;使用栈&#xff0c;一层一层的将树节点遍历入栈。 比如下面这个树&#xff0c;使用迭代法&#xff0c;1&#xff09;第一层&#xff0c;让根节点入栈。2&a…

顺序表??

1.引入&#xff1a;首先需要了解数据结构的作用&#xff1a;是计算机存储、组织数据的方式。就比如之前学的数组&#xff0c;就是一种基础的数据结构。通过数组&#xff0c;就可以去维护很多个同类型的数据&#xff0c;实现增加删除等的功能&#xff0c;这样就可以有效管理这些…

嵌入式学习63-C++

知识零碎&#xff1a; newmalloc friend 友元函数 …

微搭低代码入门01微搭底层能力介绍

目录 1 什么是应用2 有服务器和无服务器3 关系型数据库和文档型数据库4 云存储5 云函数6 静态网站托管总结 日常经常解答初学者的问题&#xff0c;问的最多的就是学习微搭&#xff0c;需要多久&#xff0c;要不要学习代码。有的初学者心比较急躁&#xff0c;总是希望一蹴而就&a…

边循环边删除List中的数据

List边循环&#xff0c;边删除&#xff1b;这种一听感觉就像是会出问题一样&#xff0c;其实只要是删除特定数据&#xff0c;就不会出问题&#xff0c;你如果直接循环删除所有数据&#xff0c;那可能就会出问题了&#xff0c;比如&#xff1a; public static void main(String[…

一些优雅的监控运维技巧

准备工作 安装 sysstat sudo apt install sysstat查看某个进程的cpu情况 pidstst -u -p 256432查看某个进程的RAM情况 pidstst -r -p 256432查看某个进程的IO情况 pidstst -d -p 256432查看某个进程下的线程执行情况 pidstst -t -p 256432查看指定PID的进程对应的可执行文件…

C++协程库封装

操作系统&#xff1a;ubuntu20.04LTS 头文件&#xff1a;<ucontext.h> 什么是协程 协程可以看作轻量级线程&#xff0c;相比于线程&#xff0c;协程的调度完全由用户控制。可以理解为程序员可以暂停执行或恢复执行的函数。将每个线程看作是一个子程序&#xff0c;或者…

OpenCV如何实现背投(58)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV直方图比较(57) 下一篇&#xff1a;OpenCV如何模板匹配(59) 目标 在本教程中&#xff0c;您将学习&#xff1a; 什么是背投以及它为什么有用如何使用 OpenCV 函数 cv::calcBackP…

蓝桥杯国赛填空题(弹珠堆放)

小蓝有 20230610 颗磁力弹珠&#xff0c;他对金字塔形状尤其感兴趣&#xff0c;如下图所示&#xff1a; 高度为 1 的金字塔需要 1 颗弹珠&#xff1b; 高度为 2 的金字塔需要 4 颗弹珠&#xff1b; 高度为 3 的金字塔需要 10 颗弹珠&#xff1b; 高度为 4 的金字塔需…

Python 语音识别系列-实战学习-语音识别特征提取

Python 语音识别系列-实战学习-语音识别特征提取 前言1.预加重、分帧和加窗2.提取特征3.可视化特征4.总结 前言 语音识别特征提取是语音处理中的一个重要环节&#xff0c;其主要任务是将连续的时域语音信号转换为连续的特征向量&#xff0c;以便于后续的语音识别和语音处理任务…

qt嵌入并控制外部程序

一、流程 1、调用Window接口模拟鼠标&#xff0c;键盘事件 POINT point; LPPOINT lpppoint &point; GetCursorPos(lpppoint);//获取鼠标位置 SetCursorPos(point.x, point.y);//设置鼠标位置//鼠标左键按下 mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, poi…

逻辑漏洞:初识水平越权与垂直越权

目录 1、什么是越权漏洞呢&#xff1f; 2、水平越权 3、垂直越权 4、burpsuite autorize插件 最近在学习逻辑漏洞的相关知识和技能&#xff0c;这里pikachu靶场作为演示进行学习一下&#xff1a; pikachu靶场&#xff1a;GitHub - zhuifengshaonianhanlu/pikachu: 一个好玩…

计算机服务器中了devicdata勒索病毒怎么办?Devicdata勒索病毒解密工具步骤

在这个网络飞速发展的时代&#xff0c;网络为企业的生产运营起到了关键性作用&#xff0c;利用网络可以开展各项工作业务&#xff0c;大大提高了企业生产效率与业务水平&#xff0c;在大家都为网络的便利感到欣慰时&#xff0c;网络数据安全问题&#xff0c;成为众多企业关心的…

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…