原题地址
最开始直接枚举起点
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.empty()) {
return 0;
}
int size = nums.size();
if(size == 1) {
return nums.back();
}
vector<int> dp(size*2);//dp(i) 前i家不包括第i家 可偷到的最大值 所以前0家必然是0 前1家也是0 所以最后拿的是dp(i+size)进行的比较
int maxN = 0;
for(int i = 0; i < size; ++i) {
for(int j = 0; j < size*2; ++j) {
dp[j] = 0;
}
for(int k = 2; k <= size; ++k) {
dp[i+k] = max(dp[i+k-1], dp[i+k-2] + nums[(i+k-1)%size]);
}
maxN = max(maxN,dp[i+size]);
}
return maxN;
}
};
但是想了想,这题和石子合并还是不一样的。
石子合并需要考虑那么多次情况,但这题受限于题目的要求,偷了一家不能再偷相邻的一家,重复计算了不少内容。
实质上只需要从0和1分别开始枚举长度就行了。
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.empty()) {
return 0;
}
int size = nums.size();
if(size == 1) {
return nums.back();
}
vector<int> dp(size*2);
int maxN = 0;
for(int i = 0; i < 2; ++i) {
for(int j = 0; j < size*2; ++j) {
dp[j] = 0;
}
for(int k = 2; k <= size; ++k) {
dp[i+k] = max(dp[i+k-1], dp[i+k-2] + nums[(i+k-1)%size]);
}
maxN = max(maxN,dp[i+size]);
}
return maxN;
}
};
又想起了可以用滚动数组优化一下空间。
顺带改善一下代码结构,使之更通用。
class Solution {
public:
int rob(vector<int>& nums) {
//边界情况
if(nums.empty()) {
return 0;
}
int size = nums.size();
if(size == 1) {
return nums.back();
}
if(size == 2) {//防止越界
return max(nums[0], nums[1]);
}
//和之前的打家劫舍思路一致 和石头合并问题 的循环解法也很一致 我最开始放弃思考了 写了个o(n2)
//但是仔细一想 其实首尾相接 和之前的打家劫舍1的 就差了一个元素
//假如我偷了第一家 那么就不能偷最后一家 如果我偷了最后一家 那么我就不能再偷第一家了
//从这个角度来看这个问题 当前问题就像是之前的缩小版本 但是需要分类讨论
//按照这种思路 最后返回的值应当是下面这种
//return max(robRange(nums, 2, size - 2) + nums[0], robRange(nums, 1, size - 3) + nums[size - 1]);
//但是如果这样写了 就是默认最后结果必然包含第一个 或者 最后一个 如2 3 2这个测试样例
//忽视掉了一些情况
/*以下为总情况
偷了第一个 没有偷最后一个
偷了最后一个 没有偷第一个
没有偷第一个 没有偷最后一个
两个变量总共4种情况
其中不可能出现 偷了第一个 偷了最后一个
要包含所有的情况 即 a = 0 || b = 0
没有偷第一个 那么最后的结果就是 在[1,size-1]中 可以偷到的最大值 因为没有偷第一个嘛
没有偷最后一个 那么最后的结果就是在 [0,size-2]中 可以偷到的最大值 因为没有偷到最后一个嘛
再从这两个里面选一个最大的作为结果即可
*/
return max(robRange(nums, 0, size - 2), robRange(nums, 1, size - 1));
}
// 偷窃指定数组中 真包含start, end下标的 元素 可达到的最大的值
int robRange(vector<int>& nums, int start, int end) {
int first = nums[start], second = max(nums[start], nums[start + 1]);
for(int i = start + 2; i <= end; ++i) {
int tmp = second;
second = max(second, first + nums[i]);
first = tmp;
//cout << first << ' ' << second << endl;
}
return second;
}
};