你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
题解:
利用动态规划,
声明 dp 数组,dp[x] 存储的值是到达当前房屋 x(包括当前房屋) 已经盗取的最大金额。由于不可连续盗取相邻房屋,而对于当前房屋 x 有可盗取或者不可盗取两种状态,所以 dp[x] 的值,要么等于当前 x 房屋的金额加上房屋 x-2 的已经盗取最大金额 dp[x-2],也就是房屋 x 盗取的状态,要么等于房屋 x-1 的盗取的最大金额,也就是房屋x 不可盗取的状态。所以 dp[x] 取两种状态的最大值。
由上可得:
- 状态转移方程:dp[x] = Max( dp[x-1], dp[x-2] + num)
代码:
class Solution {
public int rob(int[] nums) {
int len = nums.length;
// 边界条件处理
if(len == 0) return 0;
if(len == 1) return nums[0];
// 声明 dp 数组
int[] dp = new int[len];
dp[0] = nums[0]; //处理初始化,0 号房屋可盗取金额为 num[0]
dp[1] = Math.max(nums[1], nums[0]); // 1 号房间已经盗取的最大金额 max(num[1], num[0])
for(int i = 2; i < len; i ++){
dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);
}
return dp[len-1];
}
}
空间优化
在状态方程 dp[x] = Math.max(dp[x-2] + num[x], dp[x-1])
中每次我们只需要两个变量, 到达房屋 x 时(未决定盗取还是不盗取),已经盗取的最大金额 dp[x-1],和前一个盗取的的最大金额 dp[x-2]。 所有我们使用两个变量 curMax, preMax。
代码
class Solution {
public int rob(int[] nums) {
int currMax = 0; // 当前盗取的最大金额;
int preMax = 0; // 前一状态盗取的最大金额;
int temp;
for(int i = 0; i < nums.length; i ++){
temp = currMax; // 保存当前盗取的最大金额
currMax = Math.max(currMax, preMax + nums[i]); // 当前房间可盗取的最大金额
preMax = temp; // 更新 preMax
}
return currMax;
}
}