leetcode 198. House Robber

时间:2025-04-14 11:03:26

本题是动态规划问题。

第一步,明确并理解dp数组以及下标的含义

dp[i]表示从第0号房间一直到第i号房间(包含第i号房间)可以偷到的最大金额,具体怎么偷这里不考虑,第i+1号及之后的房间也不考虑。换句话说,dp[i]也就是只考虑[0,i]号房间,无论怎么偷可以偷到的最大金额。

按照这个定义,dp[n-1]就是答案。需要注意的是,dp[i]一定能求到值,不代表一定是偷了第i号房间才求得dp[i]。

第二步,明确并理解递推公式

考虑第i号房间,只有两种可能,偷或者不偷。

偷第i号房间,则第i-1号房间肯定不能偷,此时能获得的总金额为dp[i] = dp[i-2] + nums[i]。

不偷第i号房间,此时dp[i]应该等于dp[i-1]。

第三步,理解dp数组如何初始化

dp[0]应该初始化为第0号房间的金额。因为只有一间房的时候,能偷到的最大金额显然就是把它偷了。

dp[1],含义是从第0号房间和第1号房间偷,能偷到的最大金额。由于相邻的房间不能都偷,所以dp[1]= max(nums[0],numd[1]);

i>=2的dp[i]可以不初始化,或者说无论初始化为多大都没关系,因为dp[i]只和dp[i-1],dp[i-2],nums[i]有关系。

第四步,理解遍历顺序

因为dp[i]依赖于它前面的dp[i-1]和dp[i-2],所以i的遍历顺序肯定要从小到大。

代码

按照上面的思路,先初始化dp[0]和dp[1],再让i从2开始遍历,含义更加明确,代码更好理解,如下所示:

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        //dp[i]表示从第0号房间一直到第i号房间(包含第i号房间)可以偷到的最大金额。
        //按照这个定义,dp[n-1]就是答案
        vector<int> dp(n);
        dp[0] = nums[0];
        if(n < 2)
            return dp[0];
        dp[1] = max(nums[0],nums[1]);
        for(int i = 2;i < n;i++){
            //偷第i号房间
            int temp1 = dp[i-2] + nums[i];
            //不偷第i号房间
            int temp2 = dp[i-1];
            dp[i] = max(temp1,temp2);
        }
        return dp[n-1];
    }
};

但实际上,也可以不初始化让i从0开始遍历,代码如下所示:

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        //dp[i]表示从第0号房间一直到第i号房间(包含第i号房间)可以偷到的最大金额。
        //按照这个定义,dp[n-1]就是答案
        vector<int> dp(n);
        for(int i = 0;i < n;i++){
            //偷第i号房间
            int temp1 = (i >= 2 ? dp[i-2]:0) + nums[i];
            //不偷第i号房间
            int temp2 = i >= 1 ? dp[i-1]:0;
            dp[i] = max(temp1,temp2);
        }
        return dp[n-1];
    }
};