【代码随想录训练营第42期 Day39打卡 - 打家劫舍问题 - LeetCode 198.打家劫舍 213.打家劫舍II 337.打家劫舍III

本文主要是介绍【代码随想录训练营第42期 Day39打卡 - 打家劫舍问题 - LeetCode 198.打家劫舍 213.打家劫舍II 337.打家劫舍III,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、做题心得

二、题目与题解

题目一:198.打家劫舍

题目链接

题解:动态规划

题目二:213.打家劫舍II

题目链接

题解:动态规划

 题目三:337.打家劫舍III

题目链接

题解:动态规划

三、小结


一、做题心得

今天是打家劫舍的一天,来到了动态规划章节的Part7。打家劫舍问题是动态规划算法很经典的一个应用,今天将从三道题目对其进行探讨。

话不多说,直接开始今天的内容。

二、题目与题解

题目一:198.打家劫舍

题目链接

198. 打家劫舍 - 力扣(LeetCode)

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

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

示例 1:

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

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400
题解:动态规划

这是打家劫舍问题最经典的应用,我们无法打劫相邻的两个房屋,这里需要注意的是:偷取的房屋之间不一定是只隔一间房屋 -- 这一点容易出错。

对于当前房屋而言,偷与不偷取决于前一个房屋和前两个房屋是否被偷,这就说明前边状态会对当前状态造成影响 -- 这正符合动态规划的思想。

首先就是初始化:这里由于当前房屋偷与不偷受到前两天的影响,故初始化至少需要最初的两个房屋。

状态方程 -- 考虑当前状态偷与不偷:dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])

        1.前者:不偷窃第 i 间房屋,偷窃总金额为前 i−1 间房屋的最高总金额 -- 注意:这并不表示就一定要偷第 i-1 间房屋

        2.后者:偷窃第 i 间房屋,那么就不能偷窃第 i−1 间房屋,偷窃总金额为前 i−2 间房屋的最高总金额与第 i 间房屋的金额之和

代码如下:

class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();if (n == 0)     return 0;if (n == 1)     return nums[0];vector<int> dp(n, 0);       //dp[i]表示下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i](即0 - i)dp[0] = nums[0];            //初始化dp[0]与dp[1]dp[1] = max(nums[0], nums[1]);for (int i = 2; i < n; i++) {dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);    //第i个房间偷还是不偷--取较大值}return dp[n - 1];       //房间数组下标范围:0 - n-1}
};

题目二:213.打家劫舍II

题目链接

213. 打家劫舍 II - 力扣(LeetCode)

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

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

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [1,2,3]
输出:3

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000
题解:动态规划

这道题在上一道题的基础上将房屋形成了一个环--首尾相接的环。

那么整体上思路就是一样的,唯一的不同就是我们必须保证首尾两个房屋最多只能选择一个去偷。

关键:通过自定义函数将围成环的打家劫舍问题转换为不围成环的打家劫舍问题(上一道题),得到这两种情况的结果:

    1.考虑头元素,不考虑尾元素:ans1 (注意:考虑不代表一定会偷

    2.考虑尾元素,不考虑头元素:ans2

再取两者较大值即可。

这里需要注意的是:头尾元素都不取的情况有被以上两种情况的任一情况包含在内

代码如下:

class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();if (n == 0)   return 0;         //先考虑特殊情况--没有房屋或者只有一个房屋if (n == 1)   return nums[0];int ans1 = robRange(nums, 0, n - 2);       //考虑头元素,不考虑尾元素int ans2 = robRange(nums, 1, n - 1);       //考虑尾元素,不考虑头元素return max(ans1, ans2);}/*  198.打家劫舍的逻辑  */int robRange(vector<int>& nums, int start, int end) {           //自定义函数--不围成一圈的打家劫舍:start -- endint n = nums.size();if (end == start)       return nums[start];vector<int> dp(n);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];}
};

 题目三:337.打家劫舍III

题目链接

337. 打家劫舍 III - 力扣(LeetCode)

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

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

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

示例 1:

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

示例 2:

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

提示:

  • 树的节点数在 [1, 104] 范围内
  • 0 <= Node.val <= 104
题解:动态规划

这是动态规划在树的应用--树形dp--树形dp就是在树上进行递归公式的推导

题目思路很清晰,就是将打劫问题转移到了树上边来实现。

一开始,感觉很容易想到层序遍历,但是层数过多的情况,会很难判断,而且复杂度较高,行不通。再考虑递归遍历,我们要得到盗取的最大金额,需要得到有返回值的结果--首先想到后序遍历。通过动态规划实现这个问题,我们需要先得到左右节点的值,才能计算当前节点偷与不偷的结果(因为直接相连的房屋不可都进行盗取),这就引出了左右根的遍历方式--很显然,后序遍历是可行的。

如何用后序遍历实现呢?这里我们直接看灵神的的代码,通过dfs函数,通过后序遍历遍历了整棵树,再得出盗取或者不盗取当前节点能得到的最大金额--注意:这里采用pair存储这两种情况的结果,可以降低时间复杂度。

代码表示:rob:偷当前节点       rob_not:不偷当前节点

l_rob;偷左子结点       r_rob:偷右子节点

代码如下:

class Solution {
public:pair<int, int> dfs(TreeNode* cur) {     //pair类型函数:第一个元素表示偷当前节点所能得到的最大金额,第二个元素表示不偷时if(cur == nullptr) {       //当前节点为空,那么无论偷不偷这个“空节点”,结果都是0 return {0, 0};}/*   后序遍历--左右根   */auto [l_rob, l_not_rob] = dfs(cur -> left);     //左--递归遍历左子树auto [r_rob, r_not_rob] = dfs(cur -> right);    //右--递归遍历右子树int rob = l_not_rob + r_not_rob + cur -> val;       //偷当前节点时,左右子节点都不能偷int rob_not = max(l_not_rob, l_rob) + max(r_not_rob, r_rob);    //不偷当前节点,则可以偷也可以不偷左右子节点,分别取左右子树在两种状态下的较大值相加 -- 注意:在这里左右子树是分开考虑的return {rob, rob_not};}int rob(TreeNode* root) {auto [root_rob, root_not_rob] = dfs(root);      //调用dfs函数计算整棵树偷与不偷的最大金额,并返回二者中的较大值return max(root_rob, root_not_rob);}
};

三、小结

今天的打卡到此也就结束了,感觉树的遍历这一块的内容还存在着一些欠缺,后边继续加油。

这篇关于【代码随想录训练营第42期 Day39打卡 - 打家劫舍问题 - LeetCode 198.打家劫舍 213.打家劫舍II 337.打家劫舍III的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1104524

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

动态规划---打家劫舍

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

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能