题目描述
【leetcode】198. 打家劫舍( House Robber )
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
第一次解答
思路:
经过分析可以知道,当我们选中第i个元素后,下一个选中元素
只能在i+2和i+3之间(若选i+4,则必须先选i+2,否则无法利益最大),
于是我们可以通过递归的便利所有情况,返回最大值。
max_money = max(第1次选nums[0]剩下元素所得最大利益, 第1次选nums[1]剩下元素所得最大利益)
第k次选nums[i]剩下元素所得最大利益 = max(第k+1次选nums[i+2]剩下元素所得最大利益, 第k+1次选nums[i+3]剩下元素所得最大利益)
很遗憾,最后的运行时间超出限制。提速方法:把迭代的树展开,可以看到许多重复计算,这些重复计算可以去掉。
test case:
[1,2,3,1]
[2,7,9,3,1]
代码:
#include <algorithm>
class Solution {
public:
int rob_recursion(vector<int>& nums, int i){
if(i+2 >= nums.size())
return nums[i];
if(i+3 >= nums.size())
return nums[i] + nums[i+2];
return nums[i] + std::max(rob_recursion(nums, i+2), rob_recursion(nums, i+3));
}
int rob(vector<int>& nums) {
if(nums.size() == 0)
return 0;
if(nums.size() == 1)
return nums[0];
return std::max(rob_recursion(nums, 0), rob_recursion(nums, 1));
}
};
结果:
第二次解答
看了官方题解,很巧妙的方法。
思路:
动态规划
f(k)为前k家(包括第k家)房屋可得到的最大总现金,Ak表示第k家的现金
那么f(k) = max(f(k-1), f(k-2)+Ak)
代码:
#include <algorithm>
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size() == 0)
return 0;
if(nums.size() == 1)
return nums[0];
int fk_2 = nums[0]; // f(k-2)
int fk_1 = std::max(nums[0], nums[1]); // f(k-1)
int fk = fk_1;
for(int i=2; i<nums.size(); ++i){
fk = std::max(fk_1, fk_2 + nums[i]);
fk_2 = fk_1;
fk_1 = fk;
}
return fk;
}
};
结果:
第三次解答
看了这里,给递归加缓冲可以不超时。
思路:
递归方法,带缓冲
f(k)为前k家(包括第k家)房屋可得到的最大总现金,Ak表示第k家的现金
那么f(k) = max(f(k-1), f(k-2)+Ak)
代码:
#include <algorithm>
class Solution {
public:
// 前k家(包括第k家)房屋可得到的最大总现金
int rob_helpler(vector<int>& nums, int k, vector<int> &max_money){
if(k < 0 || k == 0)
return 0;
if(-1 == max_money[k-1]){
max_money[k-1] = std::max(
rob_helpler(nums, k-1, max_money),
rob_helpler(nums, k-2, max_money) + nums[k-1]);
}
return max_money[k-1];
}
int rob(vector<int>& nums) {
vector<int> max_money(nums.size(), -1);
return rob_helpler(nums, (int)nums.size(), max_money);
}
};
结果: