打家劫舍II(LeetCode 213)

简介: 打家劫舍II(LeetCode 213)

Description

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

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

Sample Input 1

nums = [2,3,2]

Sample Output 1

3

Sample Tips 1

你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

Sample Input 2

nums = [1,2,3,1]

Sample Output 2

4

Sample Tips 2

你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

Sample Input 3

nums = [1,2,3]

Sample Output 3

3

Tips

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

算法思想:

这道题目和打家劫舍是差不多的,唯一区别就是成环了。

对于一个数组,成环的话主要有如下三种情况:

  • 不包含首尾元素
  • 包含首元素,不包含尾元素
  • 包含尾元素,不包含首元素

例如情况三,虽然是考虑包含尾元素,但不一定要选尾部元素!对于情况三,取nums[1]和nums[3]就是最大的。

而情况二和情况三 都包含了情况一了,所以只考虑情况二和情况三就可以了

分析到这里,本题其实比较简单了。 剩下的和打家劫舍就是一样的了。

动规五部曲分析如下:

  1. 确定dp数组以及下标的含义

    dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]

  2. 确定递推公式

    决定dp[i]的因素就是第i房间偷还是不偷。

    如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。

    如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,(注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点

    然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);

  3. dp数组如何初始化

    从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]

    从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);

    vector<int> dp(nums.size());
    dp[0] = nums[0];
    dp[1] = max(nums[0], nums[1]);
  4. 确定遍历顺序

    dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历!

    for (int i = 2; i < nums.size(); i++) {
        dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
    }
  5. 推导dp数组

    输入 [2,7,9,3,1]

    dp数组状态图为:

    i:  0  1  2  3  4
    dp: 2  7 11 11 12

    dp[nums.size() - 1]为结果。 (12)

综上分析完毕,代码如下:

// 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了
class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size() == 0) return 0;
        if (nums.size() == 1) return nums[0];
        int result1 = robRange(nums, 0, nums.size() - 2); // 情况二
        int result2 = robRange(nums, 1, nums.size() - 1); // 情况三
        return max(result1, result2);
    }
    // 198.打家劫舍的逻辑
    int robRange(vector<int>& nums, int start, int end) {
        if (end == start) return nums[start];
        vector<int> dp(nums.size());
        dp[start] = nums[start];
        dp[start + 1] = max(nums[start], nums[start + 1]);
        for (int i = start + 2; i <= end; i++) {
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[end];
    }
};

Java代码代码如下:

class Solution {
    public int rob(int[] nums) {
        if (nums == null || nums.length == 0)
            return 0;
        int len = nums.length;
        if (len == 1)
            return nums[0];
        return Math.max(robAction(nums, 0, len - 1), robAction(nums, 1, len));
    }

    int robAction(int[] nums, int start, int end) {
        int x = 0, y = 0, z = 0;
        for (int i = start; i < end; i++) {
            y = z;
            z = Math.max(y, x + nums[i]);
            x = y;
        }
        return z;
    }
}
目录
相关文章
|
6月前
力扣337.打家劫舍3(树形dp)
力扣337.打家劫舍3(树形dp)
|
6月前
力扣198.打家劫舍(简单动态规划)
力扣198.打家劫舍(简单动态规划)
|
6月前
力扣213打家劫舍2(简单动态规划)
力扣213打家劫舍2(简单动态规划)
|
6月前
|
Java
leetcode-198:打家劫舍
leetcode-198:打家劫舍
42 0
leetcode-198:打家劫舍
|
6月前
|
Java
leetcode-213:打家劫舍 II
leetcode-213:打家劫舍 II
40 0
|
6月前
|
Java
leetcode-337:打家劫舍 III
leetcode-337:打家劫舍 III
46 0
|
6月前
|
容器
leetcode337打家劫舍3刷题打卡
leetcode337打家劫舍3刷题打卡
37 0
打家劫舍篇
打家劫舍篇
72 0