C++ day48 打家劫舍

题目1:198 打家劫舍

题目链接:打家劫舍

对题目的理解

专业小偷偷盗房屋的钱财,每个房屋存放的金额用非负整数数组表示;

如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警;

不触动警报装置的情况下,一夜之内能偷窃的最高金额;

当前房屋偷与不偷取决于前一个房屋和前两个房屋是否被偷了。即当前状态和前面状态会有一种依赖关系,那么这种依赖关系都是动规的递推公式,打家劫舍是dp解决的经典问题

动规五部曲

1)dp数组及下标i的含义

dp[i]:考虑下标i(包含下标i)之前的房间所能偷的最大的金币为dp[i]

最终结果:dp[nums[i]-1]

2)递推公式

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

偷房间i的金币:dp[i]=dp[i-2]+nums[i],第i-1个房一定是不考虑的,找出下标i-2(包括i-2)以内的房屋

不偷房间i的金币:dp[i]=dp[i-1],考虑i-1房,(注意这里是考虑,并不是一定要偷i-1房)

dp[i]=max(dp[i-2]+nums[i],dp[i-1])

3)dp数组初始化

根据递推公式看出,dp[0]和dp[1]是递推公式的基础,所以初始化为dp[0]=nums[0],dp[1]=max(nums[0],nums[1]),其余非0下标,初i始成任意值均可,因为dp[i]是从前面两个得到的,最终会被计算的结果覆盖

4)遍历顺序

i要从小到大百遍历,这样才能使用前面的值推导后面的dp[i]

for(i=2;i<nums.size();i++)

5)打印dp数组

代码

class Solution {
public:
    int rob(vector<int>& nums) {
        //定义并初始化dp数组
        vector<int> dp(nums.size(),0);
        if(nums.size()==0) return 0;
        if(nums.size()==1) return nums[0];
        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];   
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

题目2:213 打家劫舍Ⅱ

题目链接:打家劫舍Ⅱ

对题目的理解

房屋围成一圈,最后一个房屋和第一个房屋时紧挨着的,如果两间相邻的房屋在同一晚上被闯入,系统会自动报警,在不触动报警装置的情况下,偷窃到的最高金额

将环形问题转换为线性问题,因为最后一个房间和第一个房间是挨着的,所以进行分类讨论,考虑3种情况,!!!用的是"考虑",例如情况三,虽然是考虑包含尾元素,但不一定要选尾部元素!

动规五部曲

1)dp数组及下标初始化

dp[i]:考虑下标i(包含下标i)之前的房屋所能盗窃的最大金币数

2)递推公式

与打家劫舍Ⅰ的递推公式相同dp[i]=max(dp[i-1],dp[i-2]+nums[i]),只不过最终比较考虑首端元素的情况与考虑尾端元素的情况的最大值

3)dp数组初始化

与打家劫舍Ⅰ的初始化相同,dp[0]=nums[0],dp[1]=max(nums[0],nums[1])

4)遍历顺序

从小到大遍历

5)打印dp数组

将打家劫舍Ⅰ的代码封装成函数,然后比较首端和尾端取值的最大值

代码

class Solution {
public:
    int robrange(vector<int>nums,int begin,int end){
        if(end==begin) return nums[begin];
        //定义并初始化dp数组
        vector<int> dp(nums.size(),0);
        dp[begin]=nums[begin];
        dp[begin+1]=max(nums[begin],nums[begin+1]);
        //递推
        for(int i=begin+2;i<=end;i++){
            dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[end];
    }
    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);
    }
    
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

题目3:337 打家劫舍Ⅲ

题目链接:打家劫舍Ⅲ

对题目的理解

房子是二叉树的形式,两个直接相连的放在在同一晚被打劫,就会报警,返回不报警的最大金额

本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算

与打家劫舍,打家劫舍II一样,关键是要讨论当前节点抢还是不抢

如果抢了当前节点,两个孩子就不能动,如果没抢当前节点,就可以考虑抢左右孩子注意这里说的是“考虑

暴力搜索法

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int rob(TreeNode* root) {
        if(root==NULL) return 0;
        if(root->left==NULL && root->right==NULL) return root->val;
        //偷父节点
        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);
        return max(val1,val2);
    }
};
  • 时间复杂度:O(n^2),这个时间复杂度不太标准,也不容易准确化,例如越往下的节点重复计算次数就越多
  • 空间复杂度:O(log n),算上递推系统栈的空间

以上代码超时了,这个递归的过程中其实是有重复计算了,计算了root的四个孙子(左右孩子的孩子)为头结点的子树的情况,又计算了root的左右孩子为头结点的子树的情况,计算左右孩子的时候其实又把孙子计算了一遍。

记忆化递推

使用一个map把计算过的结果保存一下,这样如果计算过孙子了,那么计算孩子的时候可以复用孙子节点的结果。

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    unordered_map<TreeNode*,int> umap;//记录计算过的结果
    int rob(TreeNode* root) {
        if(root==NULL) return 0;
        if(root->left==NULL && root->right==NULL) return root->val;
        if(umap[root]) return umap[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);
        umap[root]=max(val1,val2);
        return max(val1,val2);
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(log n),算上递推系统栈的空间

动态规划

在上面两种方法,其实对一个节点 偷与不偷得到的最大金钱都没有做记录,而是需要实时计算

树形dp的入门题目,因为是在树上进行状态转移,以递归三部曲为框架,其中融合动规五部曲

递归三部曲

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

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

dp数组以及下标的含义:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱,使用一个长度为2的数组,记录当前节点偷与不偷所得到的的最大金钱。

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

2)确定终止条件

在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回,相当于dp数组的初始化

3)确定遍历顺序

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

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

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

4)确定单层递归逻辑

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

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

最后当前节点的状态就是{val2, val1}; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱},注意顺序不能颠倒,因为递归逻辑里面就是偷与不偷,不偷在前,偷在后

5)打印dp数组

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

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int rob(TreeNode* root) {
        //定义并初始化dp数组
        vector<int> dp=robtree(root);
        return max(dp[0],dp[1]);
    }
    vector<int> robtree(TreeNode* cur){
        if(cur==NULL) return vector<int>{0,0};//偷和不偷两个状态
        vector<int> left=robtree(cur->left);//左孩子
        vector<int> right=robtree(cur->right);//右孩子
        //偷cur
        int val1=cur->val+left[0]+right[0];
        //不偷kur
        int val2=max(left[0],left[1])+max(right[0],right[1]);
        return {val2,val1};//注意这里返回的顺序不要弄反,因为每一层递归需要这个顺序的返回值
    }
};
  • 时间复杂度:O(n),每个节点只遍历了一次
  • 空间复杂度:O(log n),算上递推系统栈的空间

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单3D姿态基线模型网络架构与验证【SIM】

在这篇文章中&#xff0c;我们将回顾 ICCV’17 上提出的 Simple 3D Pose Baseline &#xff0c;即用于 3d 人体姿势估计的简单而有效的基线&#xff0c;也称为 SIM。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在…

Pandas进阶:拼接 concat 使用方法

1.处理索引和轴 假设我们有2个关于考试成绩的数据集。 df1 pd.DataFrame&#xff08;{ name&#xff1a;[A&#xff0c;B&#xff0c;C&#xff0c;D]&#xff0c;math&#xff1a;[60,89,82,70]&#xff0c;physics&#xff1a;[66&#xff0c; 95,83,66]&#xff0c;chemi…

Siemens-NXUG二次开发-新建与保存prt文件[Python UF][20231204]

Siemens-NXUG二次开发-新建与保存prt文件[Python UF][20231204] 1.python uf函数1.1 NXOpen.UF.Part.New1.2 NXOpen.UF.Part.Save1.3 NXOpen.UF.Ui.OpenListingWindow1.4 NXOpen.UF.Ui.IsListingWindowOpen1.5 NXOpen.UF.Ui.WriteListingWindow1.6 NXOpen.UF.Ui.SaveListingWin…

Spring MVC学习随笔-文件下载和上传(配置文件上传解析器multipartResolver)

学习视频&#xff1a;孙哥说SpringMVC&#xff1a;结合Thymeleaf&#xff0c;重塑你的MVC世界&#xff01;&#xff5c;前所未有的Web开发探索之旅 学习视频&#xff1a;【编程不良人】继spring之后快速入门springmvc,面对SpringMVC不用慌 六、SpringMVC 文件上传下载 6.1 文件…

LeetCode(49)用最少数量的箭引爆气球【区间】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 用最少数量的箭引爆气球 1.题目 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [x_start, x_end] 表示水平直径在 x_start 和 x_end之间的气球。你不知道气…

【WPF.NET开发】创建简单WPF应用

本文内容 先决条件什么是 WPF&#xff1f;配置 IDE创建项目设计用户界面 (UI)调试并测试应用程序 通过本文你将熟悉在使用 Visual Studio 开发应用程序时可使用的许多工具、对话框和设计器。 你将创建“Hello, World”应用程序、设计 UI、添加代码并调试错误。在此期间&#…

leetcode 142.环形链表2

我来更新 leetcode 题目了&#xff0c;接着上一次&#xff0c;这一次是上一道题目的提升&#xff08;有点数学题的感觉&#xff09; 142.环形链表2 题目 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表…

CCKS2023-面向上市公司主营业务的实体链接评测-亚军方案

赛题分析 大赛地址 https://tianchi.aliyun.com/competition/entrance/532097/information 任务描述 本次任务主要针对上市公司的主营业务进行产品实体链接。需要获得主营业务中的产品实体&#xff0c;将该实体链接到产品数据库中的某一个标准产品实体。产品数据库将发布在竞赛…

RK3568平台开发系列讲解(Linux系统篇) dtb 到 device_node 的转化

🚀返回专栏总目录 文章目录 一、dtb 展开流程二、dtb 解析过程源码分析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍通过设备树 dtb 如何展开成 device_node 一、dtb 展开流程 设备树源文件编写: 根据设备树的基本语法和相关知识编写符合规范的设备树。…

工具类整理

常用工具类 在java的庞大体系中&#xff0c;其实有很多不错的小工具&#xff0c;也就是我们平常说的&#xff1a;轮子。 CollectionUtils 目前比较主流的是spring的org.springframework.util包下的CollectionUtils工具类。和apache的org.apache.commons.collections包下的Co…

根据豆瓣对《流浪地球》的短评数据进行文本分析和挖掘

1背景 2019年2月5日电影《流浪地球》正式在中国内地上映。该电影在举行首映的时候&#xff0c;口德好得出奇&#xff0c;所有去看片的业界大咖都发出了画样赞叹&#xff0c;文化学者能锦说:“中国科幻电影元年开启了。"导演徐峰则说&#xff0c;“里程碑式的电影&#xf…

实时流式计算 kafkaStream

文章目录 实时流式计算Kafka StreamKafka Streams 的关键概念KStreamKafka Stream入门案例编写SpringBoot 集成 Kafka Stream 实时流式计算 一般流式计算会与批量计算相比较 流式计算就相当于上图的右侧扶梯&#xff0c;是可以源源不断的产生数据&#xff0c;源源不断的接收数…

WEB服务器配置与HTTP分析

目录 实验目的&#xff1a; 实验要求&#xff1a; 实验原理&#xff1a; 实验步骤&#xff1a; 步骤1&#xff1a;创建拓扑 步骤2&#xff1a;为PC、Client和Server配置IPv4地址、子网掩码和域名服务器 步骤3&#xff1a;启动设备和服务器 步骤4&#xff1a;测试PC-1、C…

【Qt开发流程】之自定义语法高亮和使用HTML语法

描述 语法高亮&#xff08;Syntax Highlighting&#xff09;是一种在编辑器中突出显示代码语法元素的技术&#xff0c;使其更易于阅读和理解。 Qt提供了一个功能齐全的语法高亮框架&#xff0c;支持多种语言和格式&#xff0c;可以自定义颜色和样式。 对于使用Qt的开发人员来说…

HADOOP::Fsimage和Edits解析

NameNode被格式化之后&#xff0c;将在/opt/module hadoop-3.1.3/data/tmp/dfs/name/curent目录 中产生如下文件 fsimage_ 0000000000000000000 fsimage_ 0000000000000000000.md5 seen_txid VERSION (1) Fsimage文件: HDFS文件系统元数据的一个永久性的检查点&#xff0…

使用pytorch从零开始实现迷你GPT

生成式建模知识回顾: [1] 生成式建模概述 [2] Transformer I&#xff0c;Transformer II [3] 变分自编码器 [4] 生成对抗网络&#xff0c;高级生成对抗网络 I&#xff0c;高级生成对抗网络 II [5] 自回归模型 [6] 归一化流模型 [7] 基于能量的模型 [8] 扩散模型 I, 扩散模型 II…

机器学习决策树ID3算法

1、先去计算总的信息量 2、根据不同指标分别计算对应的信息增益 3、根据算出的信息增益来选择信息增益最大的作为根结点 4、天气中选择一个继续上述过程 5、决策树划分结束

solidity实现ERC20代币标准

文章目录 1、以太坊 - 维基百科2、IERC203、ERC204、Remix 编译部署 1、以太坊 - 维基百科 以太坊&#xff08;Ethereum&#xff09;是一个去中心化的开源的有智能合约功能的公共区块链平台。以太币&#xff08;ETH 或 Ξ&#xff09;是以太坊的原生加密货币。截至2021年12月&a…

克服.360勒索病毒:.360勒索病毒的解密和预防

导言: 在数字化的今天&#xff0c;数据安全问题变得愈发棘手。.360勒索病毒是当前网络空间的一场潜在灾难&#xff0c;对于这个威胁&#xff0c;了解应对之道和采取切实的预防措施至关重要。如果您正在经历勒索病毒的困境&#xff0c;欢迎联系我们的vx技术服务号&#xff08;s…

华为手环配置技巧

前言 华为手环作为生活健康辅助设备发挥不可忽视的作用&#xff0c;但每次更换手环后需要重新配置。华为手环不仅有健康监测、消息通知、天气推送、离线支付、公交卡、运动锻炼、等功能&#xff0c;还有倒计时、计时器、手电筒、闹钟、等小工具。下文介绍如何进行配置。 配置…
最新文章