思路
这道题目和198.打家劫舍是差不多的,唯一区别就是成环了。
对于一个数组,成环的话主要有如下三种情况:
情况一:考虑不包含首尾元素
情况二:考虑包含首元素,不包含尾元素
情况三:考虑包含尾元素,不包含首元素
注意我这里用的是"考虑",例如情况三,虽然是考虑包含尾元素,但不一定要选尾部元素! 对于情况三,取nums[1] 和 nums[3]就是最大的。
而情况二 和 情况三 都包含了情况一了,所以只考虑情况二和情况三就可以了。
代码如下:
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
if(nums.size()==2) return max(nums[0],nums[1]);
vector<int> dp1(nums.size());//情况二,不考虑尾部元素
dp1[0]=nums[0];dp1[1]=max(nums[0],nums[1]);
vector<int> dp2(nums.size());//情况三,不考虑首部元素
dp2[0]=0;dp2[1]=nums[1];dp2[2]=max(nums[1],nums[2]);
for(int ii=2;ii<nums.size()-1;ii++){
dp1[ii]=max(dp1[ii-1],dp1[ii-2]+nums[ii]);
}
for(int ii=3;ii<nums.size();ii++){
dp2[ii]=max(dp2[ii-1],dp2[ii-2]+nums[ii]);
}
return max(dp1[nums.size()-2],dp2[nums.size()-1]);
}
};
抽象化
// 注意注释中的情况二情况三,以及把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];
}
};
总结
成环之后还是难了一些的, 不少题解没有把“考虑房间”和“偷房间”说清楚。
这就导致会有这样的困惑:情况三怎么就包含了情况一了呢? 本文图中最后一间房不能偷啊,偷了一定不是最优结果。
所以本文重点强调了情况一二三是“考虑”的范围,而具体房间偷与不偷交给递推公式去抉择。
这样就不难理解情况二和情况三包含了情况一了。