一、题目描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
二、解题思路
第一步:确定dp数组(dp table)以及下标的含义
dp[i]
:考虑下标i(包括i)以内的房屋,最多可以偷窃的⾦额为dp[i]
第二步:确定递推公式
根据题目,相邻的房间同时被偷是会触碰警报的,所以说对于第i
间房屋,存在两种状态,偷还是偷:
- 如果偷的话,那么第
i-1
间房是不偷的,偷第i
间房之前上一家偷的是第i-2
间房屋,所以说dp[i] = dp[i-2] + nums[i]
即:最多可以偷到的钱的金额是dp[i-2]
加上第i
间房偷到的钱。 - 如果不偷第
i
间房屋的话,那么dp[i] = dp[i-1]
,即考虑i-1
(注意这里是考虑,并不是一定要偷)
所以递推公式是:
dp[i] = Math.max(dp[i-2]+nums[i], dp[i-1]);
第三步:dp数组如何初始化
对于nums数组中值,只需要初始化dp[0]
和dp[1]
,因为最开始这两间房子是相邻的,起步要偷的第一件房就是其中金额最大的。
dp[0]
只有一件房子表示偷这件房屋所获得金额,dp[0] = nums[0]
dp[1]
表示有两件房子,选择其中金额数大的去偷,dp[1] = Math.max(nums[0],num[1])
第四步:确定遍历顺序
dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么⼀定是从前到后遍历!
for(int i=2; i<nums.length; i++){
dp[i] = Math.max(dp[i-2]+nums[i], dp[i-1]);
}
第五步:举例推导dp数组
三、代码演示
class Solution {
public int rob(int[] nums) {
//特判
if(nums==null || nums.length==0){
return 0;
}
//当只有一件房子时候,就偷它
if(nums.length == 1){
return nums[0];
}
//创建dp
int[] dp = new int[nums.length];
//dp初始化
dp[0] = nums[0]; //数组下标从0开始的,所以dp[0]就是偷第一间房屋的钱
dp[1] = Math.max(nums[0],nums[1]); //相邻的两间房屋选金额大的偷
//遍历
for(int i=2; i<nums.length; i++){
dp[i] = Math.max(dp[i-2]+nums[i], dp[i-1]);
}
return dp[nums.length-1];
}
}