【Day34 LeetCode】动态规划DP Ⅶ 打家劫舍

embedded/2025/2/8 20:40:25/

一、动态规划DP Ⅶ 打家劫舍

1、leetcode.cn/problems/house-robber/" rel="nofollow">打家劫舍198

首先确定dp数组,dp[i]表示从0~i房间最大可以获得的金额数
然后确定dp方程,对于当前房间i,dp[i]取决于偷不偷当前房间,如果偷当前房间,则前一个房间不能包括,如果不偷当前房间,就可以包括前一个房间。dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])

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

从递推公式发现dp[i]只与前面两个数字有关,因此可以不采用数组而是采用两个变量来不断更新结果,优化空间复杂度,代码如下:

class Solution {
public:int rob(vector<int>& nums) {int pre = 0, cur = nums[0];for(int i=1; i<nums.size(); ++i){int tmp = cur;cur = max(cur, pre + nums[i]);pre = tmp;}return cur;}
};

2、leetcode.cn/problems/house-robber-ii/description/" rel="nofollow">打家劫舍Ⅱ 213

这题在上一题的基础上变成了环,很容易陷入首尾相邻的困局中。这题比较巧妙,直接忽略头部元素或者首部元素,从而破除了环,采用上一题非环的求解结果,取两者最大值即可。

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

3、leetcode.cn/problems/house-robber-iii/description/" rel="nofollow">打家劫舍Ⅲ 337

这题的数据结构变成了二叉树。关于二叉树,最先要明确的就是二叉树的遍历,由于入口是根节点,所以应该是前序遍历。
暴力递归的代码如下,会超时,因为有重复计算的结果

/*** 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==nullptr)return 0;if(root->left==nullptr && root->right==nullptr)return root->val;int v1 = root->val; // 选择当前节点if(root->left)v1 += rob(root->left->left) + rob(root->left->right);if(root->right)v1 += rob(root->right->left) + rob(root->right->right);int v2 = rob(root->left) + rob(root->right); // 不选当前节点return max(v1, v2);}
};

在此基础上添加一个记忆化搜索

/*** 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 {unordered_map<TreeNode*, int> mp;
public:int rob(TreeNode* root) {if(root==nullptr)return 0;if(root->left==nullptr && root->right==nullptr)return root->val;if(mp[root])return mp[root];int v1 = root->val; // 选择当前节点if(root->left)v1 += rob(root->left->left) + rob(root->left->right);if(root->right)v1 += rob(root->right->left) + rob(root->right->right);int v2 = rob(root->left) + rob(root->right); // 不选当前节点v1 = max(v1, v2);mp[root] = v1;return v1;}
};

采用动态规划的思想,对于一个节点,有偷与不偷两个状态

/*** 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 {vector<int> robTree(TreeNode* node){if(node==nullptr)   return vector<int>{0, 0};vector<int> left = robTree(node->left);vector<int> right = robTree(node->right);// 不偷当前节点int v1 = max(left[0], left[1]) + max(right[0], right[1]);// 偷当前节点int v2 = node->val + left[0] + right[0];return {v1, v2};}
public:int rob(TreeNode* root) {vector<int> ans = robTree(root);return max(ans[0], ans[1]);}
};

二、写在后面

打家劫舍比较经典,第三题树形dp还不熟悉


http://www.ppmy.cn/embedded/160618.html

相关文章

【Spring Boot】自动配置源码解析

目录 Spring-Boot-Starter 一、准备配置类和 Bean 对象二、自动配置条件依赖三、Bean 的参数获取 3.1 EnableConfigurationProperties 注解3.2 ConfigurationProperties 注解 四. Bean 的发现 4.1 自己项目的 Bean 扫描4.2 jar 包的 Bean 扫描 五. Bean 的加载 自动配置总结 …

RabbitMQ深度探索:死信队列

死信队列产生背景&#xff1a; RabbitMQ 死信队列俗称 备胎队列&#xff1a;消息中间件因为某种原因拒收该消息后&#xff0c;可以转移到私信队列中存放&#xff0c;死信队列也可以有交换机和路由 key 等 生产死信队列的原因&#xff1a; 消息投递到 MQ 存放&#xff0c;消息已…

通信易懂唠唠SOME/IP——SOME/IP消息格式

SOME/IP是Scalable service-Oriented MiddlewarE over IP (SOME/IP)的缩写&#xff0c;基于IP的可扩展面向服务的中间件。广泛应用于汽车行业嵌入式通信。 它是基于服务的&#xff0c;服务可以由0个或多个Event,Method,Field组成。 Event是一种单向的数据传输&#xff0c;在数…

【R】Dijkstra算法求最短路径

使用R语言实现Dijkstra算法求最短路径 求点2、3、4、5、6、7到点1的最短距离和路径 1.设置data&#xff0c;存放有向图信息 data中每个点所在的行序号为起始点序号&#xff0c;列为终点序号。 比如&#xff1a;值4的坐标为(1,2)即点1到点2距离为4&#xff1b;值8的坐标为(6,7)…

RabbitMQ 与 Kafka 的核心区别,如何选择合适的消息中间件?

在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff0c;MQ&#xff09;扮演着重要角色&#xff0c;能够解耦服务、提高系统伸缩性、增强可靠性。目前&#xff0c;RabbitMQ 和 Kafka 是两款最常见的消息中间件&#xff0c;它们虽然都能实现消息传输&…

elasticsearch(ES)简介及安装-----笔记

elasticsearch简介 ES是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。 ES结合kibana、Logstash、Beats&#xff0c;也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域。 elasticsearch是elastic stack的核心&…

【Elasticsearch】geotile grid聚合

geotile_grid聚合是 Elasticsearch 中一种用于处理地理数据的多桶聚合方式&#xff0c;它将geo_point和geo_shape类型的值分组到表示网格的桶中。以下是关于geotile_grid聚合的详细说明&#xff1a; 基本概念 • 网格划分&#xff1a;geotile_grid聚合将地理数据划分为一个稀疏…

【mysql】数据库字段设计原则

本文将分享17个关键字段设计原则&#xff0c;这些经验可规避80%的数据库设计缺陷&#xff0c;涵盖性能、扩展性、可维护性等核心维度&#xff0c;附具体场景示例&#xff1a; 一、数据类型选择&#xff1a;避免“隐形成本杀手” 1. 整数类型精确匹配 坑&#xff1a;滥用BIGIN…