动态规划--Day03--打家劫舍--198. 打家劫舍,213. 打家劫舍 II,2320. 统计放置房子的方式数

动态规划–Day03–打家劫舍–198. 打家劫舍,213. 打家劫舍 II,2320. 统计放置房子的方式数

今天要训练的题目类型是:【打家劫舍】,题单来自@灵艾山茶府。

掌握动态规划(DP)是没有捷径的,咱们唯一能做的,就是投入时间猛猛刷题。

动态规划要至少刷100道才算入门!

记忆化搜索是新手村神器。方便理解,写完之后可以转译成递推。

但是有些题目只能写递推,才能优化时间复杂度。熟练之后直接写递推也可以。

198. 打家劫舍

方法:记忆化搜索(递归)

思路:

和爬楼梯几乎是同一个模板的。爬楼梯是memo[i] = res1 + res2;,而打家劫舍是memo[i] = Math.max(res1, res2);

后来忽然发现了一个细微的差别,爬楼梯,缓存一般是开n+1位,而打家劫舍缓存一般开n位就够了。

  1. 使用memo记忆,缓存已经探索过的节点
  2. 从数组最后一个值开始往前探索
    • 递归结束判断:如果索引超出范围,则返回
    • 如果memo有值,用缓存值
    • 对于当前探索的i这个节点,能不能偷,有两个情况:
      • 情况一:偷i-1家,那么i家就不能偷(递归进去探索i-1节点)
      • 情况二:偷i-2家,那么i家也能偷(递归进去探索i-2节点)
      • 返回两种情况的较大值,顺便保存到记忆
class Solution {public int rob(int[] nums) {int n = nums.length;// 记忆(缓存),默认未使用标志为-1int[] memo = new int[n];Arrays.fill(memo, -1);// 从索引n-1开始搜索(也就是数组的最后一个值)return dfs(n - 1, nums, memo);}private int dfs(int i, int[] nums, int[] memo) {// 索引超出范围,返回if (i < 0) {return 0;}// 如果已经判断过这种情况了,直接用记忆(缓存)if (memo[i] != -1) {return memo[i];} else {// 情况一:偷i-1家,那么i家就不能偷// 情况二:偷i-2家,那么i家也能偷int res1 = dfs(i - 1, nums, memo);int res2 = dfs(i - 2, nums, memo) + nums[i];// 返回两种情况的较大值,顺便保存到记忆memo[i] = Math.max(res1, res2);}return memo[i];}
}

从dfs函数来看,一进去先判断索引是否超出范围,超出了则返回。其实是浪费多了一次资源(因为调用函数要压栈,出栈等,时间空间都变慢了)

可以改成这样:先判断索引,再考虑调用函数:

private int dfs(int i, int[] nums, int[] memo) {if (memo[i] != -1) {return memo[i];} else {// 先判断索引合法,再调用函数int res1 = 0;if (i - 1 >= 0) {res1 = dfs(i - 1, nums, memo);}// res2要赋值为nums[i]int res2 = nums[i];if (i - 2 >= 0) {res2 = dfs(i - 2, nums, memo) + nums[i];}memo[i] = Math.max(res1, res2);}return memo[i];
}

注意,这里res2要默认赋值为nums[i]。因为这是一个决策,是决定偷i-2,那么i家先偷了,再去i-2,然后发现i-2没有人住(超出索引范围),那么i家我也偷到了。

如果写成这样是错的:

private int dfs(int i, int[] nums, int[] memo) {if (memo[i] != -1) {return memo[i];} else {int res1 = 0;int res2 = 0;if (i - 1 >= 0) {res1 = dfs(i - 1, nums, memo);}if (i - 2 >= 0) {res2 = dfs(i - 2, nums, memo) + nums[i];}memo[i] = Math.max(res1, res2);}return memo[i];
}

方法:动态规划(递推)

思路:

递推要初始化,因为f[i]需要依赖f[i-1]和f[i-2],所以要初始化前两个数,还要特判n==1的情况。

class Solution {public int rob(int[] nums) {int n = nums.length;// 如果只有一家,直接偷,返回。if (n == 1) {return nums[0];}int[] f = new int[n];// 递推要初始化,因为f[i]需要依赖f[i-1]和f[i-2],所以要初始化前两个数f[0] = nums[0];f[1] = Math.max(nums[0], nums[1]);// 从第三个数开始遍历for (int i = 2; i < n; i++) {// 情况一:偷i-1家,那么i家就不能偷// 情况二:偷i-2家,那么i家也能偷int res1 = f[i - 1];int res2 = f[i - 2] + nums[i];// 偷较大值f[i] = Math.max(res1, res2);}// 最后的结果,在最后一家判断完之后,包含了所有的情况。// 所以答案在最后一个索引的位置。return f[n - 1];}
}

思路:

把f数组索引直接+2。上一篇代码的f[0]的值,放到这一题代码的f[2]的位置。

不管f[0]和f[1],直接不用他们。

class Solution {public int rob(int[] nums) {int n = nums.length;int[] f = new int[n + 2];for (int i = 0; i < n; i++) {f[i + 2] = Math.max(f[i + 1], f[i] + nums[i]);}return f[n + 1];}
}

思路:

空间优化的写法。

class Solution {public int rob(int[] nums) {int f0 = 0;int f1 = 0;for (int x : nums) {int newF = Math.max(f1, f0 + x);f0 = f1;f1 = newF;}return f1;}
}

这些都是写完之后的优化版本,如果第一次写,不要想复杂的,先按照自己内心第一思路,把题目写完先,再想优化。

213. 打家劫舍 II

方法:动态规划(递推)

思路:

  • 特判索引0位置,把剩余位置变为非循环数组进行操作。
  • 索引0位置只有两种选择,偷或者不偷。
    • 偷索引0,那么索引1和索引n-1不能偷。问题变为从索引2到n-2的非循环数组。
    • 不偷索引0。问题变为从索引1到n-1的非循环数组。
  • 取二者较大值
class Solution {public int rob(int[] nums) {int n = nums.length;// 偷索引0,那么索引1和索引n-1不能偷。问题变为从索引2到n-2的非循环数组。int res1 = nums[0] + rob1(nums, 2, n - 2);// 不偷索引0。问题变为从索引1到n-1的非循环数组。int res2 = rob1(nums, 1, n - 1);// 取二者较大值return Math.max(res1, res2);}// 上一题的空间优化版本private int rob1(int[] nums, int start, int end) {int f0 = 0;int f1 = 0;// [start,end] 左闭右闭for (int i = start; i <= end; i++) {int newF = Math.max(f1, f0 + nums[i]);f0 = f1;f1 = newF;}return f1;}
}

2320. 统计放置房子的方式数

方法:动态规划(递推)

思路:

  • 两边互相独立,求一边,最后f[n] * f[n]即可。
  • 初始化f[0]和f[1]。如果没有空地,只有不放这1种选择;如果只有一块空地,有放和不放2种选择。
  • 对于每个f[i]:
    • 情况一:如果不放f[i]的话,f[i-1]可放可不放
    • 情况二:如果放f[i]的话, f[i-2]可放可不放
    • 两种情况加起来,就是f[i]可放可不放
  • 最后返回结果时:
    • 注意,两个MOD相加,不会溢出int。但是两个MOD相乘,会溢出int
    • 所以要先转为long,相乘,取模,后再转为int
class Solution {public int countHousePlacements(int n) {// 两边互相独立,求一边,最后f[n] * f[n]即可final int MOD = 1000000007;int[] f = new int[n + 2];// 初始化。如果没有空地,只有不放这1种选择;如果只有一块空地,有放和不放2种选择。f[0] = 1;f[1] = 2;for (int i = 2; i <= n; i++) {// 情况一:如果不放f[i],f[i-1]可放可不放// 情况二:如果放f[i], f[i-2]可放可不放// 两种情况加起来,就是f[i]可放可不放f[i] = (f[i - 1] + f[i - 2]) % MOD;}// 注意,两个MOD相加,不会溢出int。但是两个MOD相乘,会溢出int// 所以要先转为long,相乘,取模,后再转为intreturn (int) ((long) f[n] * f[n] % MOD);}
}

总感觉这道题跟《打家劫舍》没什么关系,反而像是《爬楼梯》。每个位置都可爬可不爬,然后结果加起来。f[i] = (f[i - 1] + f[i - 2])。初步是这么想,多做题,回头再看看。

关于取模运算,可以看@灵艾山茶府的这篇文章:模运算的世界:当加减乘除遇上取模。

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

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

相关文章

Nuxt.js@4 中管理 HTML <head> 标签

可以在 nuxt.config.ts 中配置全局的 HTML 标签&#xff0c;也可以在指定 index.vue 页面中配置指定的 HTML 标签。 在 nuxt.config.ts 中配置 HTML 标签 export default defineNuxtConfig({compatibilityDate: 2025-07-15,devtools: { enabled: true },app: {head: {charse…

UCIE Specification详解(十)

文章目录4.5.3.7 PHYRETRAIN&#xff08;物理层重训练&#xff09;4.5.3.7.1 Adapter initiated PHY retrain4.5.3.7.2 PHY initiated PHY retrain4.5.3.7.3 Remote Die requested PHY retrain4.5.3.8 TRAIN ERROR4.5.3.9 L1/L24.6 Runtime Recalibration4.7 Multi-module Link…

电商数据的获取方式:API、爬虫、第三方服务及更多

在竞争激烈的电商领域&#xff0c;数据是驱动业务增长的关键。准确、及时地获取电商数据&#xff0c;并进行深入分析&#xff0c;能够帮助企业洞察市场趋势、优化运营策略、提升用户体验。本文将全面介绍电商数据的获取方式&#xff0c;涵盖API接口、网络爬虫技术、第三方数据服…

《WINDOWS 环境下32位汇编语言程序设计》第8章 通用对话框

Windows操作系统为一些常用功能提供了一些通用对话框&#xff08;Common Dialog Box&#xff09;&#xff0c;比如&#xff0c;在不同的应用程序中进行打开文件、选择字体、选择颜色等操作时&#xff0c;不同程序显示的对话框的模样都是一样的。另外&#xff0c;把同样的应用程…

SOME/IP-SD协议中组播IP地址和端口号应从何处获取、由谁设置?

<摘要> AUTOSAR SOME/IP-SD协议中组播通信参数的核心配置规则明确规定了在服务端传输&#xff08;Server-Transmits&#xff09;和客户端传输&#xff08;Client-Transmits&#xff09;两种模式下&#xff0c;组播IP地址和端口号应从何处获取、由谁设置&#xff0c;从而确…

DAY49打卡

追到第45天内容浙大疏锦行

十四、测试 (Testing)

Rust内置了强大的测试框架,使得编写和运行测试变得非常简单。Rust的测试系统主要包括单元测试、集成测试和文档测试。 1. 单元测试 单元测试通常放在与被测试代码相同的文件中,使用#[cfg(test)]模块和#[test]属性标记。 1.1 基本测试结构 // 在src/lib.rs或任何模块中pub…

LeetCode 刷题【56. 合并区间】

56. 合并区间 自己做 解&#xff1a;排序合并 class Solution { public:static bool compare(const vector<int> &p1, const vector<int> &p2){ //按第一个数排序return p1[0] < p2[0]; }vector<vector<int>> merge(ve…

DistributedLock 实现.Net分布式锁

在分布式系统中&#xff0c;经常会遇到多个实例同时访问同一份资源的情况&#xff0c;例如&#xff1a; • 多个服务节点同时写入数据库同一行数据• 定时任务在多个节点上同时运行&#xff0c;导致重复执行• 多实例写缓存时出现数据覆盖问题 为了解决 并发冲突 和 数据一致…

Flutter:ios打包ipa,证书申请,Xcode打包,完整流程

步骤1 - 5 为 申请ios的签名文件&#xff0c;App ID&#xff0c;证书&#xff0c;描述文件&#xff0c;并添加测试打包设备。 步骤1&#xff1a;生成证书签名文件&#xff08;打开钥匙串访问>证书助理>从证书颁发机构请求证书&#xff09; 存储后得到了一个签名文件&…

Shell 秘典(卷二)——号令延展秘术 与 流程掌控心法・if 天机判语篇精解

文章目录前言一、命令扩展详解1.1 逻辑运算符1.1.1 逻辑与运算符&#xff08;&&&#xff09;1.1.2 逻辑或运算符&#xff08;||&#xff09;1.1.3 组合使用注意事项1.2 echo 命令1.2.1 基本用法1.2.2 输出到标准错误&#xff08;stderr&#xff09;1.3 标准文件描述符&…

Agent实战教程:深度解析async异步编程在Langgraph中的性能优化

在现代Python开发中&#xff0c;异步编程已经成为提高程序性能的重要手段&#xff0c;特别是在处理网络请求、数据库操作或AI模型调用等耗时操作时。本文将通过实际的LangGraph 示例&#xff0c;深入解析async的真正作用&#xff0c;并揭示一个常见误区&#xff1a;为什么异步顺…

coalesce在sql中什么作用

COALESCE‌是SQL中的一个函数&#xff0c;用于返回参数列表中的第一个非空值&#xff0c;若所有参数均为NULL则返回NULL&#xff0c;常用于处理数据中的空值情况。 ‌核心功能与语法‌ COALESCE函数的基本语法为&#xff1a;COALESCE(expression1, expression2, ..., express…

【Rust】 6. 字符串学习笔记

一、Rust 字符串概述 Rust 字符串是 UTF-8 编码的文本序列&#xff0c;提供两种主要类型&#xff1a; &str - 字符串切片&#xff08;通常作为引用出现&#xff09;String - 动态可变的、拥有所有权的字符串 二、字符串字面量 (&str) 编译时已知大小&#xff0c;静态分…

达梦数据库-数据文件 (二)

达梦数据库-数据文件&#xff08;二&#xff09;-自动监控达梦数据库表空间使用率的 Shell 脚本 自动监控达梦数据库表空间使用率的 Shell 脚本&#xff0c;支持&#xff1a; ✅ 实时计算每个表空间的使用率✅ 设置阈值告警&#xff08;如 >80%&#xff09;✅ 支持邮件告警&…

如何用 Android 平台开发第一个 Kotlin 小程序

安装开发环境下载并安装最新版 Android Studio&#xff08;官方 IDE&#xff09;&#xff0c;安装时勾选 Kotlin 插件。确保 JDK 版本为 11 或更高。创建新项目打开 Android Studio 选择 File > New > New Project&#xff0c;选择 Empty Activity 模板。在配置界面中&am…

Java常用工具类

异常 (Exception)。程序世界并非总是完美的&#xff0c;异常处理机制就是Java为我们提供的优雅应对错误的解决方案。一、为什么需要异常处理&#xff1f;—— 从现实世界说起 想象一下现实生活中的场景&#xff1a; 开车上班&#xff1a;你计划开车去公司&#xff08;正常流程&…

AWS亚马逊云账号注册指南

AWS是全球领先的云计算平台&#xff0c;提供广泛的云服务。账号注册是开端&#xff0c;不管是用来学习、搭建个人项目&#xff0c;还是公司项目部署上线需要&#xff0c;都需要进行这一步。提醒&#xff1a;在使用账户之前&#xff0c;必须要绑定国际的信用卡&#xff1b;通过我…

云计算学习100天-第31天

Keepalived概念keepalived 是Linux下一个轻量级的高可用解决方案主要是通过虚拟路由冗余协议(VRRP)来实现高可用功能Virtual Router Redundancy Protocol起初就是为了补充LVS功能而设计的&#xff0c;用于监控LVS集群内后端真实服务器状态后来加入了VRRP的功能&#xff0c;它出…

2025年视觉、先进成像和计算机技术论坛(VAICT 2025)

会议简介 作为人工智能大数据创新发展论坛的重要分论坛&#xff0c;2025年视觉、先进成像和计算机技术论坛聚焦人工智能感知世界的核心前沿&#xff0c;将于2025年9月18-20日在中国广州广东科学馆举行。 视觉与成像技术是智能系统理解环境的关键&#xff0c;计算机技术则…