一.LeetCode198.打家劫舍
1.题目链接
https://leetcode-cn.com/problems/house-robber/
2.题目
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
3.举例
示例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 。
4.解题思路
1.显然要采用动态规划法,假设从最后一家店铺开始抢,那么只会遇到2种情况,即:抢这家店和下下家店,或者不抢这家店,即dp[n]=max{dp[n-1],dp[n-2]+arr[n]}。
2.如果采用暴力的算法,我们来分析一下:
如果我们开始抢的是第n-1家店,那么后面可以是(n-3,n-4,n-5,n-6....);
如果我们开始抢的是第n-2家店,那么后面可以是(n-4,n-5,n-6,....);
那么这两种情况显然n-3之后的n-4,n-5,n-6,....都重复计算了。显然这里有非常大的优化空间,通常我们使用空间来换时间,即用一个result数组记录每次计算的结果,这样每次情况只需要计算一次,再次遇到只需直接返回结果即可,大大优化了时间。
5.代码实现
public class HouseRobber {
public int[] result;
public int rob1(int[] arr){
result = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result[i] = -1;
}
return solve(arr, arr.length - 1);
}
public int solve(int[] arr, int index){
if (index < 0){
return 0;
}
if (result[index] > 0){
return result[index];
}
result[index] = Math.max(solve(arr, index - 1), solve(arr, index - 2) + arr[index]);
return result[index];
}
}
二.LeetCode213.打家劫舍II
1.题目链接
https://leetcode-cn.com/problems/house-robber-ii/
2.题目
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
3.举例
示例1
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例2
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
4.解题思路
1.打家劫舍II跟LeetCode198.打家劫舍不同的地方在于,第一个房屋和最后一个房屋是相邻的,那么第一个房屋和最后一个房屋不能同时打劫,也就是说要么从第一家开始劫到倒数第二家,要么从第二家开始劫到最后一家,分别算出这两种情况下的最大抢劫金额,然后取更大的就行。
2.从第一家开始劫到倒数第二家,那么从倒数第二家开始往前面劫即可。从第二家开始劫到最后一家这种情况,第一家房屋不太好处理,那直接把result[0]设为0,劫到第一家的时候直接返回result[0],也就相当于没有劫第一家。
5.代码实现
public class HouseRobber {
public int[] result;
public int rob2(int[] arr){
result = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result[i] = -1;
}
int result1 = solve1(arr, arr.length - 2);
for (int i = 0; i < arr.length; i++) {
result[i] = -1;
}
result[0] = 0;
int result2 = solve2(arr, arr.length - 1);
return result1 >= result2 ? result1 : result2;
}
public int solve1(int[] arr, int index){
if (index < 0){
return 0;
}
if (result[index] > 0){
return result[index];
}
result[index] = Math.max(solve1(arr, index - 1), solve1(arr, index - 2) + arr[index]);
return result[index];
}
public int solve2(int[] arr, int index){
if (index < 0){
return 0;
}
if (result[index] >= 0){
return result[index];
}
result[index] = Math.max(solve2(arr, index -1), solve2(arr, index - 2) + arr[index]);
return result[index];
}
}
参考:
LeetCode 198. 打家劫舍