你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你不触动警报装置的情况下,一夜之内能够偷窃到的最高金额。
动态规划问题的特点:
- 问题可以被划分为若干
重叠
子问题- 子问题可以通过已知的子问题求解,且子问题可以重复利用
- 需要一个数据结构来存储子问题的解,以便在使用时取出
为什么这题能够使用动态规划?
解题步骤:
- 将原问题转化为一个或多个子问题
- 定义问题的状态转移方程
- 设置边界条件
- 计算子问题的解并记录
- 由子问题的解得到原问题的解
此时,我们定义一个二维数组 d p [ n ] [ 2 ] dp[n][2] dp[n][2]来存储子问题的解:
n n n表示一共有 n n n个重叠子问题
d
p
[
k
]
[
0
]
dp[k][0]
dp[k][0]表示:房屋数量为
k
k
k时,且第
k
+
1
k+1
k+1家不偷窃时最大的偷窃金额
d
p
[
k
]
[
1
]
dp[k][1]
dp[k][1]表示:房屋数量为
k
k
k时,且第
k
+
1
k+1
k+1家偷窃时最大的偷窃金额
设问题【当有
k
k
k家房屋时,最大得偷窃金额】表示为
f
(
k
)
f(k)
f(k),根据上面的等式,此时
f
(
k
)
f(k)
f(k)可表示成两个子问题:
f
(
k
)
=
{
f
(
k
?
1
)
k
不偷
n
u
m
s
(
k
)
+
d
p
[
k
?
1
]
[
0
]
k
偷
f(k)=\begin{cases} f(k-1) & k不偷 \\ nums(k) +dp[k-1][0] & k偷 \end{cases}
f(k)={f(k?1)nums(k)+dp[k?1][0]?k不偷k偷?
f
(
k
?
1
)
f(k-1)
f(k?1)分别有 第
k
?
1
k-1
k?1家偷 和 第
k
?
1
k-1
k?1家不偷下对应的解,我们取最大即可:
f
(
k
?
1
)
=
M
A
X
{
d
p
[
k
?
1
]
[
0
]
,
d
p
[
k
?
1
]
[
1
]
}
f(k-1)=MAX\{dp[k-1][0],dp[k-1][1]\}
f(k?1)=MAX{dp[k?1][0],dp[k?1][1]}
最终状态转移方程,可得:
f
(
k
)
=
{
M
A
X
{
d
p
[
k
?
1
]
[
0
]
,
d
p
[
k
?
1
]
[
1
]
}
k
不偷
n
u
m
s
(
k
)
+
d
p
[
k
?
1
]
[
0
]
k
偷
f(k)=\begin{cases} MAX\{dp[k-1][0],dp[k-1][1]\} & k不偷 \\ nums(k) +dp[k-1][0] & k偷 \end{cases}
f(k)={MAX{dp[k?1][0],dp[k?1][1]}nums(k)+dp[k?1][0]?k不偷k偷?
从状态转移方程看,我们需要从左到右的更新dp数组。
最后,附上Java实现方式:
class Solution {
public int rob(int[] nums) {
//len表示有几家房子
int len = nums.length;
//定义dp数组存储子问题的解,原问题的解存储在dp[len-1]中
int dp[][] = new int[len][2];
//边界条件
dp[0][0] = 0;//第一家不偷
dp[0][1] = nums[0];//第一家偷
//从左到右依次更新
for(int i=1;i<len;i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]);
dp[i][1] = dp[i-1][0]+nums[i];
}
return Math.max(dp[len-1][0],dp[len-1][1]);
}
}