Day42 | 动态规划 :选或不选 打家劫舍打家劫舍II

ops/2024/11/14 1:19:25/

Day42 | 动态规划 :选或不选 打家劫舍&&打家劫舍II

动态规划应该如何学习?-CSDN博客

动态规划学习:

1.思考回溯法(深度优先遍历)怎么写

注意要画树形结构图

2.转成记忆化搜索

看哪些地方是重复计算的,怎么用记忆化搜索给顶替掉这些重复计算

3.把记忆化搜索翻译成动态规划

基本就是1:1转换

文章目录

  • Day42 | 动态规划 :选或不选 打家劫舍&&打家劫舍II
    • 198.打家劫舍
      • 思路分析:
      • 1.回溯 DFS
      • 2.记忆化搜索
      • 3.1:1翻译为动态规划
    • 213.打家劫舍II

198.打家劫舍

[198. 打家劫舍 - 力扣(LeetCode)](https://leetcode.cn/problems/integer-break/description/)

思路分析:

树形结构图

image-20241110122753429

先明确一下dp数组/dfs函数的含义,dp[i]就是在前i个房子里面打家劫舍,能得到的最高金额(就是题目要求的)

我们从最后一个房子倒着往前分析子问题

对于一个房子i,我们只有两种方案,选或者不选

选了的话,那我i-1就不能选了

不选的话,那我前i个房子可以得到的最大金额数量就和前i-1个房子可以得到的最大金额数量相等,因为第i个房子没选

所以可以很轻易的得出

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

即在可能的两种方案中挑选一个最大值

1.回溯 DFS

1.返回值和参数

i是房子编号

nums是房子数字

dfs返回值就是前i个房子里面打家劫舍可以得到的最大金额(和dp数组含义相同)

int dfs(int i,vector<int>& nums)

2.终止条件

可以看到我们递归到0就是叶子结点了,下标0是第一个房子,所以小于0就代表没有房子可以选了,没有房子可以选也就没有金额,就返回0

if(i<0)return 0;

3.本层逻辑

如上所说,返回两者的最大值给上层递归函数就好

return max(dfs(i-1,nums),dfs(i-2,nums)+nums[i]);

完整代码:

当然,这是超时的

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

2.记忆化搜索

就是搞一个哈希表dp,全都初始化为-1,每次返回前给哈希表dp赋值,碰到不是-1的那就是算过的,那就直接返回计算过的结果,不需要再次递归了

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

3.1:1翻译为动态规划

1.确定dp数组以及下标的含义

dp数组就是前i个房子里面可以取得的最大金额

下标就是房子编号

2.确定递推公式

忘记了原因的请看思路分析部分

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

3.dp数组如何初始化

第一种写法,dp数组的下标从0开始,在i=0和i=1我们的递推公式中会有不合法的状态,所以i需要从2开始

那么dp[0]和dp[1]就需要初始化,那么初始化为多少呢?

如果可以根据dp的含义想通如何初始化是最好的,比如前1间房子最大值就是nums[0],前两间房子最大值就是max(nums[0],nums[1])

如果想不通的话,回到回溯法把0和1带进去就完事得出结果就完事

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

会得到如下的初始化方案:

		vector<int> dp(nums.size(),0);dp[0]=nums[0];if(nums.size()>1)dp[1]=max(nums[0],nums[1]);elsereturn dp[0];

第二种写法

dp数组的下标全部都+2(注意nums数组的下标是没变的,此时dp数组的2对应的是nums的0)

这样就避免了下标是负数的情况,完美贴合了dfs回溯的方法

		vector<int> dp(nums.size()+2,0);for(int i=0;i<nums.size();i++)dp[i+2]=max(dp[i+1],dp[i]+nums[i]);

4.确定遍历顺序

后续结果需要依赖前面的计算结果,故使用从前往后遍历

for(int i=2;i<nums.size();i++)dp[i]=max(dp[i-1],dp[i-2]+nums[i]);

完整代码

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

213.打家劫舍II

[213. 打家劫舍 II - 力扣(LeetCode)](https://leetcode.cn/problems/integer-break/description/)

分两种情况,考虑是否偷 第一个房子:

如果偷 nums[0],那么 nums[1] 和 nums[nums.size()−1] 不能偷,问题变成从 nums[2] 到 nums[nums.size()−2] 的非环形版本,直接调打家劫舍的代码
如果不偷 nums[0],那么问题变成从 nums[1] 到 nums[nums.size()−1] 的非环形版本,直接调打家劫舍的代码

当然,如果元素数量<=2的话我们的begin+2直接是不合法的,那就做一个特殊处理就好

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

http://www.ppmy.cn/ops/132957.html

相关文章

【Linux 28】应用层协议 - HTTPS

文章目录 &#x1f308; 一、HTTPS 相关概念⭐ 1. 什么是 HTTPS⭐ 2. 加密 & 解密 & 密钥⭐ 3. 常见的加密方式⭐ 4. 数据摘要 & 数据指纹⭐ 5. 初识数字签名 &#x1f308; 二、HTTPS 的加密方案探究⭐ 1. 方案一&#xff1a;只使用对称加密⭐ 2. 方案二&#xff…

【MATLAB源码-第210期】基于matlab的OFDM电力线系统仿真,不同梳状导频间隔对比。三种信道估计,三种插值误码率对比

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OFDM电力线通信系统&#xff08;PLC&#xff09;是一种通过电力线传输数据的通信技术&#xff0c;利用了OFDM&#xff08;Orthogonal Frequency Division Multiplexing&#xff0c;正交频分复用&#xff09;技术的优势来提高…

网络基础(2)

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;黑客网络基础之超文本协议与内外网划分_哔哩哔哩_bilibili 本文主要介绍内网和外网的划分。 一、内网&#xff08;局域网&#xff09; 定义 内网…

半导体制造技术导论(第二版)萧宏 第三章半导体基础答案

本章要求 1. 从元素周期表上&#xff0c;至少可以认出两种半导体材料 硅Si&#xff0c;锗Ge 2. 列出n型和p型掺杂杂质 B硼&#xff0c;引入空穴&#xff0c;p型掺杂杂质 P磷、As砷&#xff0c;引入电子&#xff0c;n型掺杂杂质。 3. 说明一个二极管和一个MOS晶体管…

现场工程师日记-MSYS2迅速部署PostgreSQL主从备份数据库

文章目录 一、概要二、整体架构流程1. 安装 MSYS2 环境2. 安装postgresql 三、技术名词解释1.MSYS22.postgresql 四、技术细节1. 创建主数据库2.添加从数据库复制权限3. 按需修改参数&#xff08;1&#xff09;WAL保留空间&#xff08;2&#xff09;监听地址 4. 启动主服务器5.…

Flutter鸿蒙next 状态管理框架对比分析

在 Flutter 开发中&#xff0c;状态管理是一个非常重要且关键的主题。Flutter 中的应用状态管理直接影响着应用的性能、可维护性和开发效率。随着 Flutter 生态的成熟&#xff0c;已经出现了许多不同的状态管理方案&#xff0c;各具特色&#xff0c;适用于不同的开发场景。本文…

链表(Linkedlist)

序言 我们都了解链表是一种数据的存储结构&#xff0c;在Java使用中逻辑与c&#xff0c;c语言数据结构别无二致&#xff0c;但主要由于Java中不存在指针的说法&#xff0c;从而导致在实现过程中的代码不同&#xff0c;所以在学习的过程中我们无需过于担心&#xff0c;逻辑都是…

使用ref操作DOM(React)

一、获取指向节点的ref 1、一般方式&#xff1a; 引入useRef Hook函数&#xff0c;它用来创建可变的ref对象。一般用于访问DOM元素或在组件之间维持不变的值 import { useRef } from react; 使用useRef来声明一个myRef const myRef useRef(null); 最后&#xff0c;将myR…