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

每日一算:打家劫舍

        “打家劫舍” 是动态规划领域的经典入门题,核心矛盾是 “不能偷窃相邻房屋”,目标是找到偷窃的最高金额。本文将从问题本质出发,拆解两种主流解题思路(动态规划、空间优化),并附上 C++ 实现,帮助你理解 “状态转移” 的核心思想。

一、问题重述

题目要求

给定非负整数数组 nums,每个元素代表一间房屋的现金金额。不能同时偷窃相邻房屋(否则触发警报),计算一夜内能偷窃到的最高金额。

示例理解

  • 示例 1:nums = [1,2,3,1]
    可选方案:偷第 1+3 间(1+3=4)或第 2+4 间(2+1=3),最高金额为 4
  • 示例 2:nums = [2,7,9,3,1]
    最优方案:偷第 1+3+5 间(2+9+1=12),最高金额为 12

二、核心思路:动态规划的 “状态转移”

问题的关键是用历史状态推导当前状态:对于第 i 间房屋,只有两种选择 ——“偷” 或 “不偷”,这两种选择对应不同的前序状态。

状态定义

设 dp[i] 为 “偷窃前 i 间房屋的最高金额”(i 从 0 开始,对应 nums[0] 到 nums[i-1])。

状态转移方程

  1. 不偷第 i 间房屋:最高金额等于偷前 i-1 间的最高金额,即 dp[i] = dp[i-1]
  2. 偷第 i 间房屋:不能偷第 i-1 间,最高金额等于偷前 i-2 间的金额 + 第 i 间的金额,即 dp[i] = dp[i-2] + nums[i-1]nums[i-1] 是第 i 间的金额,因数组索引从 0 开始)。

最终,dp[i] 取两种选择的最大值:
dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])

边界条件

  • dp[0] = 0:没有房屋可偷,金额为 0;
  • dp[1] = nums[0]:只有 1 间房屋,偷它的金额。

三、解题思路与实现

思路 1:标准动态规划(清晰易懂)

按照上述状态定义和转移方程,用数组存储所有中间状态,适合初学者理解。

步骤拆解
  1. 处理特殊情况(数组长度为 0 或 1);
  2. 初始化 dp 数组,长度为 nums.size() + 1
  3. 遍历数组,按状态转移方程计算 dp[i]
  4. 返回 dp[nums.size()](偷完所有房屋的最高金额)。
C++ 实现代码
#include <iostream>
#include <vector>
#include <algorithm> // 用于 max 函数using namespace std;class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();// 特殊情况:没有房屋或只有1间房屋if (n == 0) return 0;if (n == 1) return nums[0];// dp[i]:偷前i间房屋的最高金额vector<int> dp(n + 1);// 边界条件dp[0] = 0;dp[1] = nums[0];// 遍历计算 dp[2] 到 dp[n]for (int i = 2; i <= n; ++i) {// 状态转移:不偷第i间(dp[i-1]) vs 偷第i间(dp[i-2] + nums[i-1])dp[i] = max(dp[i-1], dp[i-2] + nums[i-1]);}return dp[n];}
};// 测试代码
int main() {Solution sol;// 示例1vector<int> nums1 = {1,2,3,1};cout << "示例1输出:" << sol.rob(nums1) << endl; // 4// 示例2vector<int> nums2 = {2,7,9,3,1};cout << "示例2输出:" << sol.rob(nums2) << endl; // 12// 特殊测试用例:全0vector<int> nums3 = {0,0,0};cout << "全0测试输出:" << sol.rob(nums3) << endl; // 0return 0;
}

思路 2:空间优化(O (1) 空间)

观察状态转移方程,dp[i] 只依赖 dp[i-1] 和 dp[i-2],无需存储整个 dp 数组,只需用两个变量记录前两个状态即可,空间复杂度从 O (n) 降至 O (1)。

步骤拆解
  1. 处理特殊情况(数组长度为 0 或 1);
  2. 用 prev_prev 记录 dp[i-2](前两间的最高金额),prev 记录 dp[i-1](前一间的最高金额);
  3. 遍历数组,计算当前最高金额 curr = max(prev, prev_prev + nums[i])
  4. 更新 prev_prev 和 prev,继续遍历;
  5. 返回 prev(遍历结束后,prev 对应 dp[n])。
C++ 实现代码
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();if (n == 0) return 0;if (n == 1) return nums[0];// prev_prev:对应 dp[i-2],prev:对应 dp[i-1]int prev_prev = 0;   // 初始 dp[0] = 0int prev = nums[0];  // 初始 dp[1] = nums[0]// 遍历从第2间房屋(nums[1])开始for (int i = 1; i < n; ++i) {// 当前最高金额 = max(不偷当前间(prev), 偷当前间(prev_prev + nums[i]))int curr = max(prev, prev_prev + nums[i]);// 更新前两个状态:prev_prev 移到 prev,prev 移到 currprev_prev = prev;prev = curr;}return prev; // 最终 prev 是 dp[n]}
};// 测试代码
int main() {Solution sol;vector<int> nums1 = {1,2,3,1};cout << "示例1输出:" << sol.rob(nums1) << endl; // 4vector<int> nums2 = {2,7,9,3,1};cout << "示例2输出:" << sol.rob(nums2) << endl; // 12// 测试用例:长度为2vector<int> nums3 = {5,10};cout << "长度2测试输出:" << sol.rob(nums3) << endl; // 10return 0;
}

四、复杂度分析

解法时间复杂度空间复杂度适用场景
思路 1(标准 DP)O(n)O(n)初学者理解,需清晰状态
思路 2(空间优化)O(n)O(1)工程实践,追求空间效率
  • 时间复杂度:两种解法均需遍历一次数组(n 为数组长度),故为 O (n);
  • 空间复杂度:标准 DP 用数组存储 n+1 个状态,为 O (n);优化版仅用两个变量,为 O (1)。

五、关键注意事项

  1. 边界条件处理:必须单独处理 n=0(空数组)和 n=1(仅 1 间房屋)的情况,避免数组越界;
  2. 状态转移的逻辑:“偷当前房屋” 时,只能加前两间的最高金额(不能加前一间),这是避免相邻偷窃的核心;
  3. 空间优化的本质:识别 “当前状态仅依赖前两个状态”,用变量替代数组,减少不必要的内存占用。

六、总结

  • 两种解法的核心均是 “动态规划的状态转移”,区别仅在于空间存储方式;
  • 思路 1 适合理解动态规划的基本流程,思路 2 更适合实际开发(空间效率更高);
  • 此类 “选择类” 问题(如 “选或不选”“取或不取”),通常可通过动态规划拆解为子问题,用历史状态推导当前最优解。

文章转载自:

http://AjFGKNv2.xbmwm.cn
http://nIapMSpt.xbmwm.cn
http://NUJHYr1R.xbmwm.cn
http://4MWsveOs.xbmwm.cn
http://Rgir27D6.xbmwm.cn
http://yYz0MDos.xbmwm.cn
http://qwwREze6.xbmwm.cn
http://aGBEN8pN.xbmwm.cn
http://UWA0xom8.xbmwm.cn
http://V4my9uZs.xbmwm.cn
http://XLSHgPfv.xbmwm.cn
http://mBFzYNQk.xbmwm.cn
http://pjZauGCG.xbmwm.cn
http://7lmN7XtK.xbmwm.cn
http://TSprdLEf.xbmwm.cn
http://0fRnvg4R.xbmwm.cn
http://UNnRZuuv.xbmwm.cn
http://rSX983Uw.xbmwm.cn
http://ZSNpqsdG.xbmwm.cn
http://82VnakJa.xbmwm.cn
http://fFio7k58.xbmwm.cn
http://CMPyq7sy.xbmwm.cn
http://amJLYqrX.xbmwm.cn
http://Vc8rPPxi.xbmwm.cn
http://E7CJgTCW.xbmwm.cn
http://IieVcfAK.xbmwm.cn
http://NaFZnfrB.xbmwm.cn
http://SDBPly4i.xbmwm.cn
http://TCGtdCym.xbmwm.cn
http://MAWFfTPZ.xbmwm.cn
http://www.dtcms.com/a/379775.html

相关文章:

  • MemGPT: Towards LLMs as Operating Systems
  • MySQL与PostgreSQL核心区别对比
  • Redis基础命令速查:从连接到数据操作,新手也能上手
  • 信息安全工程师考点-网络安全法律与标准
  • 阿里云OSS vs 腾讯云COS vs AWS S3:对象存储价格与性能深度对比
  • vim复制本地到linux服务器上,换行缩进过大,不对的问题
  • 【贪心算法】day9
  • HarmonyOS 5分布式数据管理初探:实现跨设备数据同步
  • 【Unity UGUI 交互组件——InputFild(TMP版本)(11)】
  • 基于QVTKOpenGLNativeWidget的三维点云可视化实现
  • Qwen3 中注意力机制实现
  • 基于librdkafa C++客户端生产者发送数据失败问题处理#2
  • Maya绑定:渲染编辑器Hypershade简单使用,给小球添加材质纹理
  • 前端基础 —— A / HTML
  • 线性代数 | 行列式与矩阵区别
  • Redis 核心数据结构:String 类型深度解析与 C++ 实战
  • 【Linux】面试常考!Linux 进程核心考点:写时拷贝优化原理 + 进程等待实战,一篇理清进程一生
  • 根据当前门店经纬度,求出1km内的门店
  • java类冲突
  • 线上的Python服务如何部署?
  • ​​Cinema 4D 2026 核心亮点:AI智能驱动 + 无缝实时渲染​
  • 【Pywinauto库】10.7 pywinauto.controls.uia_controls控件
  • Next.js 字体优化:使用 `next/font` 告别布局偏移和性能瓶颈
  • 腾讯滑块---Js逆向酷狗音乐登入
  • 机器学习算法概述
  • zzz‘sJavaweb知识点总结
  • 【STL源码剖析】二叉世界的平衡:从BST 到 AVL-tree 和 RB-tree 的插入逻辑
  • Altium Designer使用精通教程 第四章(PCB封装库绘制)
  • 基于多模态与主动学习的车船飞机图像识别系统研究与应用技术方案
  • cesium的3dtiles模型矫正工具