动态规划(二) —— 打家劫舍系列问题总结

news2024/9/23 7:28:07

前言

        除了背包系列问题,打家劫舍系列问题同样也是动态规划的经典题目。在这篇文章中荔枝将会把打家劫舍系列问题做一下总结,再仔细体会动态规划的思想,希望能帮到有需要的小伙伴~~~


文章目录

前言

一、Leecode198.打家劫舍

1.1 分析

1.2 题解示例

二、Leecode213.打家劫舍II

2.1 分析 

2.2 题解示例

三、Leecode337.打家劫舍III

3.1 分析

3.2 题解示例

总结


一、Leecode198.打家劫舍

题目描述:

        你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

输入样例:

        [1,2,3,1]

输出样例:

        4

1.1 分析

明确dp数组含义和递推式 

        从题目的描述中我们可以了解到,相邻的房子因为有防盗系统是不能偷的,所以当前房屋偷与不偷是由前面的两个房子来决定的。弄清楚这个关系之后我们首先需要明确dp数组的含义dp[i]:考虑包括i的房屋在内最多可以偷窃的金额。第i个房屋有两个状态:偷和不偷,偷的时候dp[i] = dp[i-1]+nums[i],不偷的话就是前一个房屋被偷了dp[i] = dp[i-1],我们要取最大偷窃金额的话就需要比较这两个状态的大小并取最大值,这时候就得到了递推公式:

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

确定初始化条件和遍历顺序

        我们看到dp[i]其实是由前两个状态共同决定的,因此我们需要初始化dp[0]和dp[1]。根据dp数组的定义,dp[0] = nums[0]。由于一开始的两个房屋我们只能偷一个并让金额取得最大值,所以再i=1的时候dp[1] = max(nums[0],nums[1])。其余的下标可以初始化成一个最小的非负整数,所以初始化为0即可。最后返回的结果就是取到nums数组的最后一个元素时取到的最小金额dp[nums.size()-1]。

1.2 题解示例

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==1) return nums[0];
        vector<int> dp(nums.size(),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];
    }
};
//本题解题的key就是找到递推关系式,也就是打劫第i个房子需要由前两个状态来决定的

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/house-robber
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


二、Leecode213.打家劫舍II

题目描述:        

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

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

输入样例:

        nums = [2,3,2]

输出样例:

        3

2.1 分析 

        打家劫舍II和打家劫舍最大的的区别就是房屋围成了一圈,这样子首尾房屋就是相邻的房屋,这就会出现三种情况:偷首不偷尾、偷尾不偷首、首尾皆不偷。要想较好的来解决成环后的问题,我们首先根据这三种情况来将成环后的房屋转换成不成环的打家劫舍问题。由于第三种情况包含在前两种情况中,也就是说,我们可以将题目给出的房屋价值数组分为两类:0到nums.size()-2和1到nums.size()-1。

分析到这里,其实这道题目就转化成了Leecode198.打家劫舍问题了,本题的重点是如何将环打开以及明确打开环后的三种情况,弄懂了上面的打家劫舍问题荔枝相信这道题目还是比较容易AC出来的。

2.2 题解示例

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==1) return nums[0];
        //两种情况——考虑左节点和右节点
        int left = robb(nums,0,nums.size()-2);
        int right = robb(nums,1,nums.size()-1);
        return max(left,right);
    }
    int robb(vector<int>& nums,int start,int end){
        if(start==end) 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];
    }
};

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/house-robber-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


三、Leecode337.打家劫舍III

题目描述:

        小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

输入样例:

        root = [3,2,3,null,3,null,1]

输出样例:

        7

3.1 分析

明确dp数组的含义

        首先我们确定一下dp数组及其对应的下标的含义:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。由于这道题目和二叉树结合在一起,所以我们借助二叉树的性质确定边界条件:遇到空节点就会返回0。

确定遍历顺序

由于这道题目是需要通过递归函数的返回值来做下一步的判断和计算的,所以这里采用的是后序遍历的方式来遍历。

单层递归过程

在递归函数中,我们采用后序遍历的方式将左右节点分别求其偷与不偷的结果集,并对当前节点分成两类情况来分析,偷父节点就不能偷子节点,存储偷与不偷的结果并最后返回求最大值即可。

3.2 题解示例

/**
 * 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) {
        vector<int> result = robTree(root);
        return max(result[0], result[1]);
    }
    //[0]:不偷能够盗取到的最高金额
    //[1]:偷能够盗取到的最高金额
    //robTree会返回一个偷与不偷的结果集
    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];
        // 不偷cur,那么可以偷也可以不偷左右节点,则取较大的情况
        int val2 = max(left[0], left[1]) + max(right[0], right[1]);
        return {val2, val1};
    }
};

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/house-robber-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

        我们看到其实打家劫舍系列问题中难度其实是在逐步递增的,需要注意的其实就是打家劫舍III,它跳出了动态规划的固有思维,和二叉树的求解方法融合在了一起,确实也比较难想到求解的方式。荔枝感觉其实动态规划更像是一个黑箱,你可以只需要给出dp推导式和遍历顺序,初始化后就可以干活了。难度还是很大的,也可能是因为荔枝正处于初级阶段吧哈哈哈哈哈。总的来说打家劫舍III还是需要好好注意一下这种思维方式,当然了后续在股票投资问题也是会有相应的处理过程哈哈。


总结

        在这篇文章中,荔枝把动态规划中地打家劫舍系列问题总结完成,个人感觉刷完题目再来写总结Blog确实收获很大,之前不太清晰的地方也渐渐地弄清楚了,大家继续跟荔枝一起加油吧哈哈哈哈哈~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

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

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

相关文章

C2C、B2B、B2C、O2O电商定义区别

如今是电子商务高度发展的时代&#xff0c;大多数传统企业商家都开始采取转型战略帮助企业发展&#xff0c;商业模式的选择是转型的关键点&#xff0c;下面小编向你详细介绍一下B2B、B2C、C2C、O2O这些商务模式&#xff0c;帮助你快速理解它们。 BBusiness&#xff0c;即企业;…

Servlet实现文件下载的功能

download.html <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>文件下载</title><base href"<%request.getContextPath()"/"%>>"> </head>…

前端基本功 用 React Hooks + Antd 实现一个 Todo-List

背景 使用 React Hooks 以及组件库 Antd 来实现一个可以 增删 标记是否完成 的 todo-list 思路 要实现一个 todo-list 首先想到用 useState 维护一个状态数组来保存当前 list &#xff0c;还要用一个状态维护添加框中的内容 const [todos, setTodos] useState(initialValu…

OpenCv图像基本变换

目录 一、图像翻转 二、图像旋转 三、仿射变换之平移 四、仿射变换之获取变换矩阵 五、仿射变换之透视变换 一、图像翻转 图像翻转不等同于旋转&#xff0c;类似于一些视频的拍摄&#xff0c;拍摄后实际是左右颠倒的&#xff0c;通过图像翻转可进行还原 案例代码如下: …

第十四章:Context Encoding for Semantic Segmentation ——语义分割的上下文编码

0.摘要 近期的研究在使用全卷积网络&#xff08;FCN&#xff09;框架改善像素级标注的空间分辨率方面取得了显著进展&#xff0c;通过采用扩张/空洞卷积、利用多尺度特征和细化边界等方法。本文通过引入上下文编码模块来探索全局上下文信息对语义分割的影响&#xff0c;该模块捕…

springMVC的开发步骤-15

第一步你用到前端核心控制器&#xff0c;你应该有他&#xff0c;因此导包&#xff1a; 第二步:配servlet 第三步&#xff1a;配控制器&#xff0c;用来调用视图 结束之后我要干嘛&#xff1f;这个springMvC,我能否将这个pojo放到容器内部当中&#xff0c;能否通过bean标签进行配…

Linux 系统编程-开发环境(三)

目录 16 vim 16.1 vi简介 16.1.1 命令行模式 16.1.2 文本输入模式 16.1.3 末行模式 16.2 vim基础操作 16.3 vim分屏操作 16.4 vim打造IDE 16.4.1 简洁版IDE 17 gcc 18 toolchain 19 静态库和共享库 19.1 静态库 19.2 共享库 19.2.1 基础班使 19.2.2 就业班使用…

BiSeNetv2(pytorch)测试、训练cityscapes

1. 源码&#xff1a; github: https://github.com/CoinCheung/BiSeNet git clone https://github.com/CoinCheung/BiSeNet.git2. 预训练模型&#xff1a; 工程下载后解压&#xff0c;并在其中创建文件夹【model】用于存放预训练模型&#xff1b; 3. 运行demo conda creat…

【安装】Linux环境下的 JDK的安装 安装配置 环境变量

目录 Linux安装java环境1.检查系统中是否有JDK2.删除原有的java相关的包3.上传jdk到指定的文件夹4.解压jdk-8u371-linux-x64.tar.gz5.配置jdk的环境变量6.让配置生效 运行一个jar包运行jar包进行测试 Oracle 甲骨文中国 | 云应用和云平台 windows环境下的 JDK官网下载 & 安…

Android实现抓包 使用Fiddler抓包

Fiddler下载&#xff1a;https://pan.baidu.com/s/1QzvL0xugp6XX7gbFBFlEJg?pwdbf6u 提取码&#xff1a;bf6u 1.Fiddler配置 如果用不到https可以不开启https抓包 电脑打开fiddler&#xff0c;手机电脑同一网段。fiddler打开https抓包(Tools->Options->HTTPS)&a…

re学习(17)Bugku-re-Tworld(使用DFS解决迷宫问题)

下载地址&#xff1a; 跳转提示 参考视频&#xff1a; 【Bugku/CTF/Re/WP】使用DFS解决迷宫问题CTF "Tworld"_哔哩哔哩_bilibili 载入IDA后发现有UPX壳&#xff0c;先用工具进行脱壳&#xff0c;然后载入IDA进行分析。 编写脚本&#xff1a; #全部代码 import…

【电路原理学习笔记】第3章:欧姆定律:3.3 电压的计算

第3章&#xff1a;欧姆定律 3.3 电压的计算 电压相关欧姆定律公式&#xff1a; V I R VIR VIR 【例3-11】在图3-10所示的电路中&#xff0c;产生5A电流需要多大电压&#xff1f; 【答】 V I R 5 A 100 Ω 500 V VIR5\rm A\times 100Ω500 V VIR5A100Ω500V 【同步练习…

N天爆肝数据库——MySQL(5)

本文主要对索引进行了讲解 这里写目录标题 本文主要对索引进行了讲解索引概述介绍优缺点索引结构二叉树红黑树B-Tree(多路平衡查找树)BTreeBTree与B-Tree区别: HashHash索引特点 为什么InnoDB存储引擎选择使用BTree索引结构&#xff1f;索引分类在InnoDB存储引擎中&#xff0c;…

详解JS的四种异步解决方案!

目录 同步&异步的概念 js中异步的应用场景 实现异步的四种方法 1、 回调函数 2、Promise 3、Generator 4、 async/await 「异步编程」是前端工程师日常开发中经常会用到的技术&#xff0c;也是校招面试过程中常考的一个知识点。 通过掌握「异步编程」的四种方式&…

redis 和mongodb基础操作练习

目录 redis作业 string、list、hash 数据类型 举例说明list和hash的应用场景&#xff0c;每个至少一个场景 mongodb作业 1. 创建一个数据库 名字grade 2. 数据库中创建一个集合名字 class 3. 集合中插入若干数据 文档格式如下 4. 查找 5. 增加、更新、删除、统计 re…

【电路原理学习笔记】第3章:欧姆定律:3.1 电压、电流与电阻的关系

第3章&#xff1a;欧姆定律 3.1 电压、电流与电阻的关系 欧姆定律指出&#xff1a;电流与电压成正比&#xff0c;与电阻成反比。即 I V R I\frac{V}{R} IRV​ 3.1.1 电压与电流之间的线性关系 数学上&#xff0c;线性指的是变量之间的关系在图形上是一条直线。线性方程所对…

数据结构与算法——希尔排序(引例、希尔增量序列、原始希尔排序、代码、时间复杂度、Hibbard增量序列、Sedgewick增量序列)

目录 引例 希尔增量序列 原始希尔排序 代码&#xff08;C语言&#xff09; 时间复杂度 更多增量序列 Hibbard增量序列 Sedgewick增量序列 希尔排序&#xff08;by Donald Shell&#xff09; 引例 给以下序列进行排序&#xff1a; 先以5的间隔进行插入排序&#xff1a…

自学数据结构和算法(5)

二叉树的遍历 分为先序、中序、和后序遍历。 这三种遍历都可以由递归序来得到&#xff1a; &#xff08;1&#xff09;先序遍历&#xff08;也是二叉树的深度优先遍历&#xff09;是第一次到某个结点才打印&#xff1b; &#xff08;2&#xff09;中序遍历是第二次到某个结…

【ACM】—蓝桥杯大一暑期集训Day3

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前正在学习C/C、Java、算法等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&#xff…

PCL 获取点集中距离最近的两个点(二维)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 给定一个平面上n个点的点集,问题是找出点集中最近的点对。这个问题经常出现在许多应用程序中,例如,在空中交通管制中,监视靠得太近的飞机,因为这可能发生碰撞。这里实现一种方式来获取这两个点,具体计算过程如…