本题是动态规划问题。
第一步,明确并理解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];
}
};