刚刚刷了一道题,是House Robber的升级版,看似简单其实细节方面还是很要命的。下面就和大家分享一下经验吧!
题目如下:
题意分析:
给定一个表示每个房屋放有多少金钱的数组且将第一个房子与最后一个房子连在一起形成环状(也即第一个元素与最后一个元素看做相连),在不触动报警系统的情况下(偷窃连着的两个房子即会触动报警系统),请问你最多能偷到多少钱?
方法一(记忆化搜索法)
由于房子排成了一个环状,如果抢了第一家,就不能抢最后一家,所以第一家和最后一家只能抢其中的一家,或者都不抢。于是可以把问题转化一下,考虑先把第一家去掉,抢一次得到最大值,再把最后一家去掉,再抢一次得到最大值,最后比较两次抢到的最大值,并将较大的一次作为最终所求结果。代码其实大体与 House Robber 是一致的。
解题代码如下:
class Solution{
private:
vector<int> memo;
int findrob( vector<int>& nums, int index, int length ){
if( index >= length ) return 0;
if( memo[index] != -1) return memo[index];
int res = 0;
for ( int i = index; i < length; i++ ) {
res = max( res, nums[i] + findrob(nums, i+2, length) );
}
memo[index] = res;
return res;
}
public:
int rob( vector<int>& nums ){
memo = vector<int>(nums.size(), -1);
//如果数组中只有一个元素,那么分两次抢就会返回0
if ( nums.size() <= 1 ) return nums.size() == 1 ? nums[0] : 0;
int res = findrob( nums, 0, nums.size() - 1 );
//由于每次抢房子均需要初始化memo,所以两次抢房子必须分开
memo = vector<int>(nums.size(), -1);
//比较两次抢房子的结果,并返回最大值
return max( res, findrob( nums, 1, nums.size()) );
}
};
提交后的结果如下:
方法二(动态规划法)
在“https://blog.csdn.net/Vensmallzeng/article/details/99546616”的基础上,改变抢房子的范围,分两次抢即可。
解题代码如下:
class Solution{
public:
int rob( vector<int>& nums ){
int n = nums.size();
if ( nums.size() <= 1 ) return nums.size() == 1 ? nums[0] : 0;
//memo1用于第一次抢
vector<int> memo1(n, -1);
memo1[n-1] = nums[n-1];
//在[i,...n-1]范围内偷房子
for (int i = n - 2; i >= 1; i--) {
//考虑从j=i开始偷,依次遍历
for (int j = i; j < n; j++) {
memo1[i] = max( memo1[i], nums[j] + (j + 2 < n ? memo1[j+2] : 0 ));
}
}
//memo2用于第二次抢
vector<int> memo2(n, -1);
memo2[n-2] = nums[n-2];
//在[i,...n-2]范围内偷房子
for (int i = n - 3; i >= 0; i--) {
//考虑从j=i开始偷,依次遍历
for (int j = i; j < n - 1; j++) {
memo2[i] = max( memo2[i], nums[j] + (j + 2 < n -1 ? memo2[j+2] : 0 ));
}
}
//比较两次抢房子的结果并返回最大值
return max( memo1[1], memo2[0]);
}
};
提交后的结果如下:
日积月累,与君共进,增增小结,未完待续。