题目
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
题解
方法一:动态规划(递归实现)
算法
解这道题的思路采用动态规划的思想
设第 i i i个房屋的现金为 A i A_i Ai,有 n n n个房屋时能够偷窃到的最高金额为 f ( n ) f(n) f(n),则:
- 如果有一户人家,那答案就是本身,即 A 0 A_0 A0
- 如果有两户人家,那就是 m a x ( A 0 , A 1 ) max(A_0, A_1) max(A0,A1)
- 如果有三户人家,则为 m a x ( A 0 + A 2 , A 1 ) max(A_0+A_2, A_1) max(A0+A2,A1)
由此可以归纳出:
f
(
n
)
=
m
a
x
(
f
(
n
−
2
)
+
A
n
,
f
(
n
−
1
)
)
f(n)=max(f(n-2)+A_n,f(n-1))
f(n)=max(f(n−2)+An,f(n−1))
从上式可以看出符合动态规划的全局最优解蕴含在局部最优解的思想
实现算法的时候可以参照斐波那契数列的递归实现方法,也是最简单能想到的一个方法
代码
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 0)
return 0;
return Rob(nums, nums.size() - 1);
}
int Rob(vector<int>& nums, int n) {
if (n == 0)
return nums[0];
else if (n == 1)
return nums[0] > nums[1] ? nums[0] : nums[1];
int a = Rob(nums, n - 2) + nums[n];
int b = Rob(nums, n - 1);
return a > b ? a : b;
}
};
分析
简单递归实现大量重复计算底层元素
- 时间复杂度: O ( 2 n ) O(2^n) O(2n)
- 空间复杂度: O ( n ) O(n) O(n),递归树的深度可以达到 n n n
- LeetCode排名:超时
方法二:动态规划(数组实现)
算法
算法思路也是动态规划:
f
(
n
)
=
m
a
x
(
f
(
n
−
2
)
+
A
n
,
f
(
n
−
1
)
)
f(n)=max(f(n-2)+A_n,f(n-1))
f(n)=max(f(n−2)+An,f(n−1))
不过实现的时候没有使用递归,而是从前往后将每次的最优解都存在数组里
代码
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return 0;
else if (n == 1)
return nums[0];
else if (n == 2)
return nums[0] > nums[1] ? nums[0] : nums[1];
int* dp = new int[n];
dp[0] = nums[0];
dp[1] = nums[0] > nums[1] ? nums[0] : nums[1];
for (int i = 2; i < n; i++) {
int a = dp[i - 2] + nums[i];
int b = dp[i - 1];
dp[i] = a > b ? a : b;
}
return dp[n - 1];
}
};
分析
- 时间复杂度: O ( n ) O(n) O(n),一次遍历即可
- 空间复杂度: O ( n ) O(n) O(n),创建了n大小的空间
- LeetCode排名: 4 m s 4ms 4ms
方法三:动态规划(常数空间)
算法
算法思路也是动态规划:
f
(
n
)
=
m
a
x
(
f
(
n
−
2
)
+
A
n
,
f
(
n
−
1
)
)
f(n)=max(f(n-2)+A_n,f(n-1))
f(n)=max(f(n−2)+An,f(n−1))
可以注意到,计算第
i
i
i个最优解
f
(
i
)
f(i)
f(i)时,只需要
f
(
i
−
2
)
f(i-2)
f(i−2)和
f
(
i
−
1
)
f(i-1)
f(i−1),故可以对上面的算法进行改进
存储上摒弃了新开辟线性空间的做法,而是只用了两个变量来存储这两个最优解
代码
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size() == 0)
return 0;
int pre = 0, cur = 0; //previous and current maximum result
for (const auto& i : nums) {
int t = cur;
cur = cur > pre + i ? cur : pre + i;
pre = t;
}
return cur;
}
};
分析
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1),只用常数空间
- LeetCode排名: 0 m s 0ms 0ms,有时跳到 4 m s 4ms 4ms