手摸手提桶跑路——打家劫舍系列

234 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

打家劫舍Ⅰ

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

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

来源:力扣(LeetCode) 链接:leetcode.cn/problems/ho… 

示例 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

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

解题思路

碰到这种类型的题目,我们第一反应是想到使用动态规划。

我们设:

① dp[i]:如果我们偷窃第i个房屋的话,能偷窃到的最大金额。

  1. 如果我们要偷的房屋只有一个,那么可偷窃的最高金额必然是1号房屋中藏着的钱。此时 dp[0] = nums[0]
  2. 如果我们要偷的房屋有两个,鉴于我们不能偷窃相邻的两个房屋,那么可偷窃的最高金额必然是1号房屋和2号房屋中所藏金额中最高的那间。此时dp[1] = Math.max(nums[0], nums[1])
  3. 如果我们要偷的房屋有三个,鉴于我们不能偷窃相邻的两个房屋,所以我们能偷的房屋只有1号+3号一组,2号一组,共两组方案。那么此时我们能偷窃的最高金额是dp[2] = Math.max(nums[0]+nums[2], nums[1]) = Math.max(dp[0]+nums[2], dp[1]);
  4. 前面每一步,都是当下能得到的最优解,由当前最优解一步步得到全局最优解。

② 得到状态转移方程

由①的逐步分析,我们可以发现如果我们偷第i个房间时,能偷到的最高金额,是Math.max(偷到第i-1个房间能偷到的最高金额,偷到第i-2个房间能偷到的最高金额+第i个房间的金额)。根据这个推理我们可以得到dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i])

题解

var rob = function(nums) {
    const lens = nums.length;
    if(lens === 1) return nums[0];
    const dp = [];
    dp[0] = nums[0]; // ①1
    dp[1] = Math.max(nums[0], nums[1]); // ①2
    for(let i=2; i<lens; ++i) {
        dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]); // ②
    }
    return dp[dp.length-1];
};

最终能偷到的最大金额就是dp的最后一项的值。

打家劫舍Ⅱ

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。 

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

来源:力扣(LeetCode) 链接:leetcode.cn/problems/ho… 

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4

示例 3:

输入:nums = [1,2,3]
输出:3

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

解题思路

因为第一个房屋和最后一个房屋是紧挨着,那么就意味如果我们偷了第一个房屋,就不能偷最后一个房屋;如果我们偷了最后一个房屋,就不能偷第一个房屋。由此可知,假设有n个房屋,那么我们能偷到的最大金额是Math.max([房屋2, 房屋n], [房屋1, 房屋n-1]);

题解

/** * @param {number[]} nums
 * @return {number}
 */
const getMax = (nums) => {
    const lens = nums.length;
    const dp = [];
    dp[0] = nums[0];
    dp[1] = Math.max(nums[0], nums[1]);
    for(let i=2; i<lens; ++i) {
        dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);
    }
    return dp[dp.length-1];
}
var rob = function(nums) {
    const lens = nums.length;
    if(lens === 1) return nums[0];
    if(lens === 2) return Math.max(nums[0], nums[1]);
    return Math.max(getMax(nums.slice(1)), getMax(nums.slice(0, -1)));
};