问题描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
解题报告
最简单粗暴的动态规划
创建一个二维数组 dp[size][2]
。
dp[i][1]表示偷窃第i个房屋得到的最高金额。
dp[i][0]表示不偷窃第i个房屋得到的最高金额。
所以转移方程为:
d
p
[
i
]
[
1
]
=
d
p
[
i
−
1
]
[
0
]
+
n
u
m
s
[
i
]
dp[i][1]=dp[i-1][0]+nums[i]
dp[i][1]=dp[i−1][0]+nums[i]
d
p
[
i
]
[
0
]
=
m
a
x
(
d
p
[
i
−
1
]
[
0
]
,
d
p
[
i
−
1
]
[
1
]
)
dp[i][0]=max(dp[i-1][0],dp[i-1][1])
dp[i][0]=max(dp[i−1][0],dp[i−1][1])
优化版动态规划
但实际上,我们不需要二维矩阵即可实现。
dp[i]
表示偷到第 i
个房屋所能得到的最高金额。当偷第 i
家房舍时,第 i-1
家房舍就不能偷,此时,dp[i]=dp[i-1]+nums[i]
;当不偷第 i
家房舍时,则是否偷窃第 i+1
家房舍不受影响,此时,dp[i]=dp[i-1]
。最终取两者的最大值即可。
转移方程为:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
d
p
[
i
−
2
]
+
n
u
m
s
[
i
]
)
dp[i]=max(dp[i-1],dp[i-2]+nums[i])
dp[i]=max(dp[i−1],dp[i−2]+nums[i])
滚动数组版动态规划
在上一个方法的基础上,由于dp[i]只受到前两个状态的影响,所以我们可以使用大小为3的滚动数组来循环存储最新的三个值。
转移方程为: d p [ i % 3 ] = m a x ( d p [ ( i − 2 ) % 3 ] + n u m s [ i ] , d p [ ( i − 1 ) % 3 ] ) dp[i\%3]=max(dp[(i-2)\%3]+nums[i],dp[(i-1)\%3]) dp[i%3]=max(dp[(i−2)%3]+nums[i],dp[(i−1)%3])
在实现上,为了让循环从i=0开始,所以我们将转移方程改为:
d
p
[
(
i
+
3
%
3
]
=
m
a
x
(
d
p
[
(
i
−
2
+
3
)
%
3
]
+
n
u
m
s
[
i
]
,
d
p
[
(
i
−
1
+
3
)
%
3
]
)
dp[(i+3\%3]=max(dp[(i-2+3)\%3]+nums[i],dp[(i-1+3)\%3])
dp[(i+3%3]=max(dp[(i−2+3)%3]+nums[i],dp[(i−1+3)%3])
最后返回
d
p
[
(
n
u
m
s
.
s
i
z
e
(
)
−
1
+
3
%
3
]
dp[(nums.size()-1+3\%3]
dp[(nums.size()−1+3%3] 即可。
实现代码
最简单粗暴的动态规划实现
class Solution {
public:
int rob(vector<int>& nums) {
int n=nums.size();
if(n==0)
return 0;
vector<vector<int>>dp(n, vector<int>(2,0));
dp[0][1]=nums[0];
dp[0][0]=0;
for(int i=1;i<n;i++){
dp[i][1]=dp[i-1][0]+nums[i];
dp[i][0]=max(dp[i-1][1],dp[i-1][0]);
}
return max(dp[n-1][1],dp[n-1][0]);
}
};
优化版动态规划实现
int rob(vector<int>&nums){
int n=nums.size();
vector<int>dp(n);
if(n==0) return 0;
if(n==1) return nums[0];
if(n==2) return max(nums[1], nums[0]);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<n;i++){
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[n-1];
}
滚动数组版动态规划实现
int rob(vector<int>&nums){
vector<int>dp(3,0);
for(int i=0;i<nums.size();i++){
dp[(i+3)%3]=max(dp[(i+1)%3]+nums[i],dp[(i+2)%3]);
}
return dp[(nums.size()+2)%3];
}
总结
针对这种数组类 拿 还是 不拿 的题目。我们不妨从最简单的思路开始,首先创建一个二维数组,第二维分别表示到第 i 个数,拿 产生的价值;不拿 产生的价值,接下来看是否有优化的空间。