一、需求
- 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
- 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
二、DFS
class Solution {
public int rob(int[] nums) {
/*
1.
状态:房间的索引,它是一直变化的,从0到nums.length-1
选择:对于当前房间,偷或者不偷
2.
dp函数:dp(nums, start),从房间nums[start]开始做选择,可以获得
的最多金额数
3.
base case:start >= nums.length,即没有房间了,返回金额0
4.
目标:dp(nums, 0)
*/
if(nums == null || nums.length == 0) {
return 0;
}
return dp(nums, 0);
}
public int dp(int[] nums, int start) {
if(start >= nums.length) {
return 0;
}
int select = dp(nums, start + 2) + nums[start];
int noSelect = dp(nums, start + 1);
return Math.max(select, noSelect);
}
}
三、带有"备忘录"的递归
class Solution {
public int rob(int[] nums) {
/*
1.
状态:房间的索引,它是一直变化的,从0到nums.length-1
选择:对于当前房间,偷或者不偷
2.
dp函数:dp(nums, start),从房间nums[start]开始做选择,可以获得
的最多金额数
3.
base case:start >= nums.length,即没有房间了,返回金额0
4.
目标:dp(nums, 0, memo)
*/
if(nums == null || nums.length == 0) {
return 0;
}
int[] memo = new int[nums.length];
Arrays.fill(memo, -1);
return dp(nums, 0, memo);
}
public int dp(int[] nums, int start, int[] memo) {
if(start >= nums.length) {
return 0;
}
if(memo[start] != -1) {
return memo[start];
}
int select = dp(nums, start + 2, memo) + nums[start];
int noSelect = dp(nums, start + 1, memo);
int res = Math.max(select, noSelect);
memo[start] = res;
return res;
}
}
四、动态规划(自底向上)
class Solution {
public int rob(int[] nums) {
/*
1.
状态:房间的索引,它是一直变化的,从0到nums.length-1
选择:对于当前房间,偷或者不偷
2.
dp函数:dp[i],从第i间房开始做选择,可以获得
的最多金额数
3.
base case:dp[n] = 0,对于第n间房间,能偷的金额为0
4.
目标:dp[0]
5.
转移方程:选择偷:dp[i] = dp[i + 2] + nums[i],即在第i间房子偷
了之后,拥有的钱是当前偷到的钱+第i+2~第n间房子偷的钱
选择不偷:dp[i] = dp[i + 1],即第i间房子不偷,那么拥有
的钱是第i+1~第n间房子偷得钱
*/
int n = nums.length;
int[] dp = new int[n + 2];
// 从最后一间房子开始往前偷
for(int i = n - 1; i >= 0; i--) {
dp[i] = Math.max(dp[i + 1], dp[i + 2] + nums[i]);
}
return dp[0];
}
}
五、动态规划优化
class Solution {
public int rob(int[] nums) {
/*
1.
状态:房间的索引,它是一直变化的,从0到nums.length-1
选择:对于当前房间,偷或者不偷
2.
dp函数:dp[i],从第i间房开始做选择,可以获得
的最多金额数
3.
base case:dp[n] = 0,对于第n间房间,能偷的金额为0
4.
目标:dp[0]
5.
转移方程:选择偷:dp[i] = dp[i + 2] + nums[i],即在第i间房子偷
了之后,拥有的钱是当前偷到的钱+第i+2~第n间房子偷的钱
选择不偷:dp[i] = dp[i + 1],即第i间房子不偷,那么拥有
的钱是第i+1~第n间房子偷得钱
*/
int n = nums.length;
int pre_1 = 0;
int pre_2 = 0;
int cur = 0;
// 从最后一间房子开始往前偷
for(int i = n - 1; i >= 0; i--) {
cur = Math.max(pre_1, pre_2 + nums[i]);
pre_2 = pre_1;
pre_1 = cur;
}
return cur;
}
}