198.打家劫舍

😄198.打家劫舍

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

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

思路

基本思想

求一夜之间的最大金额,并且相邻的房屋不能同时访问,所以要考虑的问题就是如何遍历提供的nums数组

  1. 记录奇数和偶数的和,哪个大返回哪个?

这个思路看起来可以,但是遇到[2,1,1,2]会出错,不一定要隔一次选一个,有可能隔两次,三次

  1. 当前房屋偷不偷,看偷之后形成的总金额有没有不偷高

$$ dp[j]=max(dp[j-2]+nums[j],dp[j-1]) $$

其中dp[i]代表下标为[0,i]的房屋中能够获得的最大金额是多少

当前房屋如果偷了,前一家就不能偷,主要是看相邻的两个房屋偷谁

核心思想就是当前房屋偷了之后性价比高不高,站在当前的角度看前面,先不管后面的情况

按照公式更新即可

执行流程

  1. 初始化dp数组全为0,便于后面的统计
  2. 注意nums数组只有一个元素的情况,因为统计时考虑的是前后两个元素,所以一个元素的情况需要单独处理
  3. 按照递推公式统计
  4. 返回最终的结果

代码

按照以上分析,得出以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    //并不是简单的偷奇数或者偶数,有可能隔好几个房间才偷一次,例如[2,1,1,2]
    //核心理念就是相邻的两个房屋只能偷一个
    int rob(vector<int>& nums) {
        //只有一个房间,那么这个房间肯定要被偷
        if(nums.size()==1)
            return nums[0];
        //到这里至少两个房间
        vector<int> dp(nums.size(),0);
        dp[0]=nums[0];
        dp[1]=max(nums[1],nums[0]);
        for(int i=2;i<nums.size();++i){
            //偷当前房间划算还是偷当前房间的前一个房间划算
            //dp[i]代表下标为[0,i]的房屋中能够得到的最大金额
            //更新时,相当于邻居的邻居和自己的金额与邻居的金额作比较
            dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[nums.size()-1];
    }
};

总结

主要是要想到当前房屋和隔壁房屋的关系,只能偷一个,所以需要判断偷哪个性价比高,以此形成了递推公式,最终的结果累加到了dp数组的最后一个元素中