你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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 。
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
int len=nums.size();
if(len==1){
return nums[0];
}
vector<int> dp=vector<int>(len+1,0);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<len;i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[len-1];
}
};
class Solution {
public int rob(int[] nums) {
int len = nums.length;
int[][] dp = new int[len+1][2];
if (len > 0){
dp[0][1] = Math.max(0,nums[0]);
}else {
return 0;
}
for (int i = 1; i < len ; i++) {
dp[i][1] = Math.max(dp[i-1][0],dp[i-1][0]+nums[i]);
dp[i][0] = Math.max(dp[i-1][1],dp[i-1][0]);
}
return Math.max(dp[len-1][1],dp[len-1][0]);
}
}
这题与之前的面试题 17.16. 按摩师类似,如何从按摩师转型自由职业者??
由于结果由之前的元素决定题目,这里我们可以考虑使用动态规划解决问题。
######二维数组
先从简单的思路推导,可以看到我们在java的写法明显不同于C++,这里java的解法便于理解
我们维护一个二维数组dp,dp[i][1]
维护在偷取i房间偷窃金额,dp[i][1]
表示没有偷窃的金额
dp[i][1]
如果小偷在i
房间进行了犯罪,那么他i-1
天不能进行犯罪,那么犯罪还是不犯罪?这是一个问题!!
如果犯罪,那么当前犯罪金额就是 dp[i-1][0]+nums[i]
,如果不犯罪,那当前犯罪金额就是dp[i-1][0]
,我们从其中一个选出最大值
dp[i][1] = Math.max(dp[i-1][0],dp[i-1][0]+nums[i]);
dp[i][0]
如果当小偷在i房间,突然觉悟,不能犯错误,那么他之前的当前的犯罪金额就要从上一个i-1
房间获取其中一个最大值,即
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]);
遍历结束后,最后我们从二维数组找出小偷最终的犯罪记录,按照其最大的金额作为证据,,接受人民的审批,接受正义的审批!!
return Math.max(dp[len-1][1],dp[len-1][0]);
######一维数组
上一个思路,我们使用二维数组维护,小偷在某个房间犯罪还是不犯罪的最大金额。那么我们从二维数组的解法进行提炼。
我们假设目前存在>2数量的房间
1.当小偷准备第 i 间房屋犯罪,那么就在第 i-1 间房屋犯罪,那么他犯罪金额就要从i-2的房间+i房间的金额进行计算
2.不偷窃第i 间房屋,犯罪金额为前i-1 间房屋的最大金额和。
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
dp[0]
和dp[1]
可以先进行取值,遍历指标从2开始,避免i-2超出数组的范围
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<len;i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}