JS Leetcode 198. 打家劫舍 题解分析,再次感受动态规划的魅力
壹 ❀ 引
本题来自LeetCode198. 打家劫舍,难度中等,也很有意思,是一道教小偷如何偷窃最大金额的题,题目描述如下:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
- 0 <= nums.length <= 100
- 0 <= nums[i] <= 400
让我们简单分析题意,然后想办法实现它。
贰 ❀ 动态规划
本题其实是一道标准的动态规划题目,以局部最优解来求出全局最优解。假设给你一个非有序的数组,让你求出数组中的最小值,不允许排序,不允许使用Math.min你还能怎么做呢?这里就可以借用动态规划。
假设有个数组[3,0,2,4,1],我们可以假设数组第0位就是最小值(min),然后开始从第一位开始遍历(i=1),比较nums[i]与min,如果nums[i]更小,那我们就更新min,反之不用更新,i自增。

因为min的存在,我们要知道到第i位的最小值,其实只需要比较num[i]和min谁更小即可,因为min已经包含了i-1之前所有位数的最小值,这大概就是一个动态规划最基本的例子。
let findMin = function (nums) {
let min = nums[0];
for (let i = 1; i < nums.length; i++) {
if (nums[i] < min) {
min = nums[i];
}
};
return min;
}
让我们回到问题本身,提取下题目信息,小偷不能连着偷两家,所以从偷第一家的时候,就存在两种情况。我们把这个问题先简单化,比如[1,4,2]。
第一种情况,小偷从第一家开始偷,然后偷第三家。
第二种情况,小偷从第二家开始偷。
我们假设到dp[i]是能偷到的最大收益,偷到第三家的时候,能偷到的最大收益是以上两种偷法之间的最大值,也就是:
// i=2时
// dp[i-1]是直接偷第二家的收益
// dp[i-2]是直接偷第一家的收益
dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i]);
翻译过来就是,偷到第三家时,是第一家的收益加上第三家自己(nums[2])大,还是直接偷第二家的收益大。
为了能让整个数组套用上面的动态转移方程,当我们遍历时,i得从2开始,那这就有个问题,假设我们数组一共就2位[1,4],我们定义一个dp数组也是2位,很明显当i=1时,i-2越界了。

没关系,我们可以故意让dp数组多一位,目的就是为了解决这个越界问题,比如这样:

为了套用动态转移方程,我们初始化了dp[0]为0,当偷到第二家时,其实就是求dp[i] = Math.max(1,0 + 4),4更大。虽然有点魔幻,但找出动态转移方程,套用公式,这就是动态问题的一般解决思路。
让我们实现这段代码:
/**
* @param {number[]} nums
* @return {number}
*/
let rob = function (nums) {
let n = nums.length;
if (n === 0) {
return 0;
};
if (n === 1) {
return nums[0];
};
// 在nums长度基础上加1,因为我们要预设一个0便于套公式
let dp = new Array(n + 1);
dp[0] = 0;
dp[1] = nums[0];
// i从2开始套公式
for (let i = 2; i <= n; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
};
return dp[n];
};
需要注意的有两个点是,第一个是i<=n而不是i<n,原因很简单,比如我们传入[1,4],i一开始就是2,2<2不满足,我们都无法求出偷第二家收益是多少。第二个点是,我们最终加上的是[nums[i-1]],因为i=2其实是站在dp数组的角度多加了一位,对于nums自身而言,下标最大才是1,所以需要减去1才是正确的对应关系。
我们在前面说,因为套用公式为了满足i-2,所以i从2开始,其实还有另一种做法,比如当i=1时,其实是在偷第二家,假设数组为[1,4],此时就是在区分到底是偷第一家划算还是第二家划算,所以我们可以在i<2之前专门做额外的处理,当i超过2之后再套用公式,比如这样:
let n = nums.length;
if (n === 0) {
return 0;
};
if (n === 1) {
return nums[0];
};
// 因为对于i<2做了额外处理,这里就不额外创建空间了
let dp = new Array(n);
// dp数组直接与nums对齐
dp[0] = nums[0];
for (let i = 1; i < n; i++) {
if (i < 2) {
// 小于2,那就是第一家和第二家比较收益
dp[i] = Math.max(dp[i - 1], nums[i]);
} else {
// 超过2家,套公式
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
};
// 这里是dp的最后一位,所以是长度-1
return dp[n - 1];
思路其实完全相同,只是对于2的处理方式不同,那么本文就到这里了。
JS Leetcode 198. 打家劫舍 题解分析,再次感受动态规划的魅力的更多相关文章
- [LeetCode] 198. 打家劫舍II ☆☆☆(动态规划)
描述 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金.这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的.同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的 ...
- LeetCode 198. 打家劫舍(House Robber) 5
198. 打家劫舍 198. House Robber 题目描述 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两 ...
- [LeetCode] 198. 打家劫舍 ☆(动态规划)
描述 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给定一个 ...
- Java实现 LeetCode 198 打家劫舍
198. 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报 ...
- [LeetCode]198. 打家劫舍(DP)
题目 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给定一个 ...
- leetcode 198打家劫舍
讲解视频见刘宇波leetcode动态规划第三个视频 记忆化搜索代码: #include <bits/stdc++.h> using namespace std; class Solutio ...
- Leetcode——198. 打家劫舍
题目描述:题目链接 这道题目也是一道动态规划的题目: 分析一道动态规划的题目可以将解决问题的思路分为下面三个部分: 1:问题的描述.可以定义数组d[ i ] 用于表示第i -1家可以获得的最大金额. ...
- leetcode 198 打家劫舍 Python 动态规划
打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给定 ...
- LeetCode 198. 打家劫舍(House Robber)LeetCode 213. 打家劫舍 II(House Robber II)
打家劫舍 题目描述 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报 ...
- 力扣Leetcode 198. 打家劫舍
打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给定 ...
随机推荐
- Liunx运维(二)-文件与目录操作
文档目录: 一.pwd:显示当前位置 二.cd:切换目录 三.tree:树形结构显示目录 四.mkdir 创建目录 五.touch:创建空文件或改变文件时间戳 六.ls:显示目录下内容相关属性信息 七 ...
- PMP2023
敏捷迭代周期过程中的会议 https://blog.csdn.net/xudahai513/article/details/125216704 https://img-blog.csdnimg.cn/ ...
- python3查看文件是否存在,以及读、写与执行的属性
技术背景 在使用python对系统文件进行操作的项目中,经常需要用到对本地文件的存在和读写进行判断的操作.最常用的比如os.exists函数,可以很方便的判断给定的文件名是否存在于系统中.但是这里我们 ...
- C# WPF:快把文件从桌面拖进我的窗体来!
首发公众号:Dotnet9 作者:沙漠之尽头的狼 日期:202-11-27 一.本文开始之前 上传文件时,一般是提供一个上传按钮,点击上传,弹出文件(或者目录选择对话框),选择文件(或者目录)后,从对 ...
- C# WPF 简单自定义菜单切换动画
微信公众号:Dotnet9,网站:Dotnet9,问题或建议,请网站留言: 如果您觉得Dotnet9对您有帮助,欢迎赞赏 C# WPF 简单自定义菜单切换动画 内容目录 实现效果 业务场景 编码实现 ...
- P5733 【深基6.例1】自动修正
1.题目介绍 2. 题解 2.1 字符串大小写转换 思路 str[i] -= 'a' -'A'; 注意这里转换方式,即减去偏移量(ASCII码表中,'a'在'A'前面,如果记不得偏移量,就直接写'a' ...
- VS中多个源文件中只运行其中特定文件
1.问题 有时候一个项目中创建了多个源文件,但是我只想运行其中的一个,一起运行就会出现多个main入口的问题 2.解决方式 2.1 右键要排除的文件,点击属性 2.2 从生成中排除一项中选择是即可 2 ...
- idea报错 "cannot access ..."的解决办法
File -> Invalidate Caches -> Invalidate and Restart
- 战略设计- DDD
随着系统的增长,它会变得越来越复杂,当我们无法通过分析对象来理解系统的时候,就需要掌握一些操纵和理解大模型的技术了.本文将介绍一些原则.遵循这些原则,就可以对非常复杂的领域进行建模.大部分这样的决策都 ...
- [转帖]使用 TiUP cluster 在单机上安装TiDB
https://zhuanlan.zhihu.com/p/369414808 TiUP 是 TiDB 4.0 版本引入的集群运维工具,TiUP cluster 是 TiUP 提供的使用 Golan ...