壹 ❀ 引

本题来自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. 打家劫舍 题解分析,再次感受动态规划的魅力的更多相关文章

  1. [LeetCode] 198. 打家劫舍II ☆☆☆(动态规划)

    描述 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金.这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的.同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的 ...

  2. LeetCode 198. 打家劫舍(House Robber) 5

    198. 打家劫舍 198. House Robber 题目描述 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两 ...

  3. [LeetCode] 198. 打家劫舍 ☆(动态规划)

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

  4. Java实现 LeetCode 198 打家劫舍

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

  5. [LeetCode]198. 打家劫舍(DP)

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

  6. leetcode 198打家劫舍

    讲解视频见刘宇波leetcode动态规划第三个视频 记忆化搜索代码: #include <bits/stdc++.h> using namespace std; class Solutio ...

  7. Leetcode——198. 打家劫舍

    题目描述:题目链接 这道题目也是一道动态规划的题目: 分析一道动态规划的题目可以将解决问题的思路分为下面三个部分: 1:问题的描述.可以定义数组d[ i ] 用于表示第i -1家可以获得的最大金额. ...

  8. leetcode 198 打家劫舍 Python 动态规划

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

  9. LeetCode 198. 打家劫舍(House Robber)LeetCode 213. 打家劫舍 II(House Robber II)

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

  10. 力扣Leetcode 198. 打家劫舍

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

随机推荐

  1. HP笔记本(Inspiron 7472) 视频时无摄像头的处理方案

    需要视频考试的前一天,测试摄像头时发现微信视频时竟然摄像头打不开.比较焦急,尝试了好几种办法,并在2小时内找出解决方案.   一.查看设备是否被禁用 我的电脑->右键->设备管理器-> ...

  2. 在Linux上安装和使用免费版本的PyMol

    技术背景 PyMol是一个类似于VMD的分子可视化工具,也是在PyQt的基础上开发的.但是由于其商业化运营,软件分为了教育版.开源版和商业版三个版本.其中教育版会有水印,商业版要收费,但是官方不提供开 ...

  3. 缓存选型:Redis or MemCache

    ★ Redis24篇集合 1 背景 互联网产品为了保证高性能和高可用性,经常会使用缓存来进行架构设计.最常用的就是使用Redis了,也有部分企业会选择使用Memcache. 所以了解 Redis 和 ...

  4. 【Gerrit】操作技巧

    多笔提交依赖 1. cherry-pick 同步的多笔代码前后有依赖,如第M笔提交是基于第N笔修改的,直接同步过去会有冲突,所以同步M笔提交时需要基于N笔提交,即写入第N笔commit-id 上述填的 ...

  5. Go-连接redis

  6. [转帖]9.2 TiFlash 架构与原理

    9.2 TiFlash 架构与原理 相比于行存,TiFlash 根据强 Schema 按列式存储结构化数据,借助 ClickHouse 的向量化计算引擎,带来读取和计算双重性能优势.相较于普通列存,T ...

  7. Oracle使用临时表与直接关联的性能比较

    Oracle使用临时表与直接关联的性能比较 摘要 自己的数据库水平还是太low了. 之前有很多店理解过. 但是一直理解的不深入. 比如我们这边有很多使用临时表存储中间结果数据 然后对结果数据进行关联查 ...

  8. 【转帖】Seccomp、BPF与容器安全

    语音阅读2022-06-30 20:26 本文详细介绍了关于seccomp的相关概念,包括seccomp的发展历史.Seccomp BPF的实现原理已经与seccomp相关的一些工具等.此外,通过实例 ...

  9. [转帖]SPECjvm2008 User's Guide

    SPECjvm2008 User's Guide https://spec.org/jvm2008/docs/UserGuide.html#UsePJA Version 1.0Last modifie ...

  10. Linux用户以及ssh安全相关设置

    Linux用户相关操作 摘要 最近重保, 需要进行网络安全防护. 部分同事处理过程总是顺序有一些不太对的情况. 同时发现自对Linux用户设置也存在很多不清不楚的地方 所以趁着周末学习和总结一下. 用 ...