当前位置: 首页 > news >正文

两道中等 DP 题拆解:打家劫舍 完全平方数

目录

前言

一、打家劫舍(LeetCode 198)

题目描述

核心思路:一维 DP 的状态转移

状态定义

转移方程

边界条件

代码实现(Java 版)

关键知识点

二、完全平方数(LeetCode 279)

题目描述

核心思路:完全背包 DP

状态定义

转移方程

边界条件

代码实现(Java 版)

关键知识点


前言

动态规划(DP)的进阶阶段,两道经典中等题是绕不开的:一道是「打家劫舍」,考验一维 DP 的状态转移与边界处理;另一道是「完全平方数」,结合了 BFS 和完全背包的思想,能帮你打开 DP 的解题视野。

本文从题目分析、状态定义、转移方程、代码实现一步步讲透,帮你掌握这类题的通用解题模板。


一、打家劫舍(LeetCode 198)

题目描述

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

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

核心思路:一维 DP 的状态转移

这道题的核心矛盾是:不能同时偷相邻的房子,因此对于第i间房,我们有两种选择:

  1. 偷第i间房:那么第i-1间房一定不能偷,总金额 =dp[i-2] + nums[i]
  2. 不偷第i间房:那么最高金额就是偷前i-1间房的最高金额,即dp[i-1]
状态定义

dp[i]表示偷前i间房能获得的最高金额。

转移方程

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

边界条件
  • dp[0] = nums[0](只有一间房,直接偷)
  • dp[1] = max(nums[0], nums[1])(两间房,偷金额高的那间)

代码实现(Java 版)

java

运行

public class HouseRobber { // 基础版(数组存储) public int rob(int[] nums) { if (nums == null || nums.length == 0) return 0; if (nums.length == 1) return nums[0]; int[] dp = new int[nums.length]; dp[0] = nums[0]; dp[1] = Math.max(nums[0], nums[1]); for (int i = 2; i < nums.length; i++) { dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]); } return dp[nums.length - 1]; } // 优化版(滚动变量,空间O(1)) public int robOptimized(int[] nums) { if (nums == null || nums.length == 0) return 0; if (nums.length == 1) return nums[0]; int prevPrev = nums[0]; int prev = Math.max(nums[0], nums[1]); for (int i = 2; i < nums.length; i++) { int curr = Math.max(prev, prevPrev + nums[i]); prevPrev = prev; prev = curr; } return prev; } public static void main(String[] args) { HouseRobber solution = new HouseRobber(); int[] nums = {1,2,3,1}; System.out.println(solution.rob(nums)); // 输出:4(偷1+3) System.out.println(solution.robOptimized(nums)); // 输出:4 } }

关键知识点

  • 时间复杂度:O (n),仅遍历一次数组
  • 空间复杂度:O (n),可优化到 O (1)(只保留前两个状态)
  • 面试拓展:后续变种题「打家劫舍 II(环形房屋)」「打家劫舍 III(二叉树结构)」,核心思路都是状态转移的变种。

二、完全平方数(LeetCode 279)

题目描述

给你一个整数n,返回和为n的完全平方数的最少数量。完全平方数是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,14916都是完全平方数,而311不是。

核心思路:完全背包 DP

这道题本质是完全背包问题

  • 背包容量:n
  • 物品:所有小于等于n的完全平方数(如1,4,9,16...
  • 目标:用最少的物品(平方数)装满背包
状态定义

dp[i]表示和为i的完全平方数的最少数量。

转移方程

对于每个i,遍历所有小于等于i的完全平方数j*j,则:dp[i] = min(dp[i - j*j] + 1)

边界条件

dp[0] = 0(和为 0 时,需要 0 个平方数)其余dp[i]初始化为无穷大,后续更新最小值。

代码实现(Java 版)

java

运行

public class PerfectSquares { public int numSquares(int n) { int[] dp = new int[n + 1]; // 初始化,设置为无穷大 for (int i = 1; i <= n; i++) { dp[i] = Integer.MAX_VALUE; } dp[0] = 0; // 边界条件 // 遍历所有数 for (int i = 1; i <= n; i++) { // 遍历所有小于等于i的完全平方数 for (int j = 1; j * j <= i; j++) { dp[i] = Math.min(dp[i], dp[i - j*j] + 1); } } return dp[n]; } // BFS 解法(拓展思路) public int numSquaresBFS(int n) { Queue<Integer> queue = new LinkedList<>(); Set<Integer> visited = new HashSet<>(); queue.offer(0); visited.add(0); int level = 0; while (!queue.isEmpty()) { int size = queue.size(); level++; for (int i = 0; i < size; i++) { int curr = queue.poll(); // 尝试加上所有完全平方数 for (int j = 1; j * j <= n; j++) { int next = curr + j*j; if (next == n) return level; if (next < n && !visited.contains(next)) { visited.add(next); queue.offer(next); } } } } return -1; } public static void main(String[] args) { PerfectSquares solution = new PerfectSquares(); System.out.println(solution.numSquares(12)); // 输出:3(4+4+4) System.out.println(solution.numSquaresBFS(12)); // 输出:3 } }

关键知识点

  • 时间复杂度:O (n√n),外层遍历 n,内层遍历√n 个平方数
  • 空间复杂度:O (n),存储 DP 数组
  • 拓展思路:BFS 解法(按层遍历,第一次到达 n 时的层数就是最少数量),更直观但空间复杂度更高。
http://www.jsqmd.com/news/655386/

相关文章:

  • SAP与Concur通信中断?别慌!手把手教你用STRUST搞定SSL证书过期(附Concur证书下载)
  • DSView开源仪器软件:5步快速上手的完整指南
  • Rust编程基础课 第2课时:Rust基础语法(变量、数据类型、运算符)
  • Photon光影包:如何在Minecraft中实现电影级视觉效果的终极指南
  • Chrome for Testing实战指南:构建稳定可靠的自动化测试环境
  • 告别变量地狱:Simulink大型模型参数管理的结构体实战指南(含Bus对象配置)
  • RDPWrap完全指南:免费解锁Windows多用户远程桌面完整教程
  • 为什么你的ChatBI总答非所问?深度拆解知识库向量化失效的3类隐性数据腐化场景
  • 从零开始:Ultimaker Cura 3D打印切片软件完全指南
  • SukiUI 主题配置实用技巧:从入门到精通的完整配置指南
  • ROS多相机部署实战:基于roslaunch的4种RealSense相机配置策略详解
  • 从单体到微前端:我们如何用Qiankun+Vue3重构一个老后台的样式隔离难题
  • Matlab进阶:如何通过pchip_pro实现自定义导数的Hermite分段三次插值
  • 基于STC89C52的智能避障循迹小车优化与扩展功能实现
  • 别再死记硬背斐波那契了!用‘爬楼梯’这个生活例子,5分钟彻底搞懂动态规划的核心思想
  • MusePublic实战案例:单款白衬衫,如何一键生成7种风格变体
  • 3分钟搞定Figma中文界面:设计师的终极语言解决方案
  • Python生物信息学完全指南:从零开始掌握基因组数据分析
  • 别让电压和温度坑了你!BL24C128A/512A EEPROM环境可靠性测试全记录与驱动避坑指南
  • PX4开发环境搭建:从QGC地面站编译到连接SITL仿真的完整链路实践
  • 如何一键检测微信单向好友:WechatRealFriends免费工具终极使用指南
  • 第16篇:长短期记忆网络(LSTM)——解决RNN“遗忘症”的良方(原理解析)
  • Smart Connections:如何用本地AI嵌入技术重塑知识连接体验
  • Linux驱动调试实战:xl9535中断风暴的定位与修复
  • 实战STM32驱动VS1053:从零构建MP3播放器的核心代码与调试
  • STM32实战指南:GUI-Guider与LVGL无缝对接的界面开发全流程
  • 极修师上门服务费用贵得离谱吗,好用的上门服务品牌推荐指南 - 工业推荐榜
  • 2026届学术党必备的十大AI科研助手解析与推荐
  • 2026年实测:Gemini 3 Pro中文能力深度拆解与国内免费镜像站推荐
  • 3个步骤掌握英雄联盟回放分析:ROFL播放器新手完全指南