213.打家劫舍II

😄213.打家劫舍II

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

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

思路

基本思想

相比于 198题 ,本题只是多了一个限制条件,头尾也变成邻居,也就是第一个房屋被偷,最后一个房屋就不能被偷,每偷一个房间,至少隔一个房间才能继续偷

影响解题的因素就是头尾两个房间连起来了,所以我们将其分开,分情况讨论:

  1. 去掉头部元素
  2. 去掉尾部元素

去掉之后可以将其当成198题进行统计,由于统计的过程中情况1有可能不选择尾部元素,情况2有可能不选择头部元素,所以同时不选择头尾的元素这种情况已经被包含了,剩下的就是198题的逻辑

执行流程

按照情况分开,分别对情况1和情况2进行统计,最终返回大的结果

  1. 初始化dp数组
  2. 集体处理只有一个和两个元素的情况
  3. 按照递推公式进行统计
  4. 返回最终的结果

代码

根据以上分析,得出以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
    int rob(vector<int>& nums) {
        //这两句不能忘掉,否则会出现下标越界的情况
        if (nums.size() == 1) return nums[0];
        if (nums.size() == 2) return max(nums[0],nums[1]);
        //到这里至少三个元素
        int result1 = robRange(nums, 0, nums.size() - 1); 
        int result2 = robRange(nums, 1, nums.size() ); 
        return max(result1, result2);
    }
    // 198.打家劫舍的逻辑
    int robRange(vector<int>& nums, int start, int end) {
        vector<int> dp(nums.size());
        dp[start] = nums[start];
        //如果start=1时,这句初始化应该有问题
        //因为dp[i]代表的是下标为[o,start]的房屋中能够获取到的最大金额
        //而start=1时,dp[start+1]代表房屋的范围为[0,2]一共三个房屋
        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-1];
    }
};

总结

主要是将头尾相接的问题分情况讨论,注意同时去掉头尾的情况已经包含在了情况1和情况2中

本题中dp数组按照下标进行更新,而不是按照元素在第几进行更新是因为去掉尾部时,元素以0开头,去掉头部时元素以1开头,开头的元素下标不固定,所以只能按照下标去访问

按照下标去访问,可以将不同区间的元素统一处理