LeetCode动态规划基础之——打家劫舍I II分析(Java)
动态规划的核心思想就是把问题变小,“大事化小”。
举个例子:比如想求得规模为N的问题的结果,就先求出规模为N-1的问题的结果,用这种层层递进的方法求出最终的最优结果。
打家劫舍I
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,
影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,
如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,
计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 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 。
解答思路:定义一个数组存储前n个房子可盗取的最大金额。
比如:前j个房子盗取的最大金额有两种情况
①第j-1个房子没盗取,那么就可以盗取第j个房子,这种情况盗取的金额是dp[j-2] + nums[j]
②第j-1个房子盗取了,那么第j个房子就不可以被盗取了,这种情况盗取的金额是dp[j-1]
那么上面这两种情况,哪种情况盗取的金额最多,前j个房子可以盗取的金额就是哪种情况,代码表示就是:dp[j] = Math.max(dp[j-1],dp[j-2]+nums[j]),完整代码如下:
import java.util.*;
class Solution {
public int rob(int[] nums) {
if(nums == null || nums.length == 0) return 0;
int[] dp = new int[nums.length];//偷窃n家房屋获得的金额最大值
if(nums.length == 1) return nums[0];
dp[0] = nums[0];
dp[1] = Math.max(nums[0],nums[1]);
for(int j = 2; j < nums.length; j ++){
dp[j] = Math.max(dp[j-1],dp[j-2]+nums[j]);
}
return dp[nums.length - 1];
}
}
打家劫舍II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。
这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。
同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入
系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
解答思路:和上一题相比没什么太大的变动,唯一的区别就是这里房子成环形,说白了就是第一个房子和最后一个房子是挨着的,因此在偷盗过程中有两种选择情况:
①偷盗第一家房子,那么最后一家房子,一定不可以偷盗了
②跳过第一家房子,不偷~,这种情况,最后一家房子偷不偷是不确定的,这取决于一共有多少家房子。
既然分为上面两种情况讨论,那么就需要建立两个dp数组,分别存储两种偷盗方式的前N家房子可以得到的最大偷盗金额。最后比较两种方式那个金额最大,就返回哪个。
!!这里面需要注意:return时候,dp1是一定不偷最后一家的,所以dp1的最大金额是存储在nums.length-1数组下标处。完整代码如下:
class Solution {
public int rob(int[] nums) {
if(nums == null || nums.length == 0)
return 0;
if(nums.length == 1)
return nums[0];
if(nums.length == 2)
return Math.max(nums[0],nums[1]);
int[] dp1 = new int[nums.length];//一定不偷最后一家
int[] dp2 = new int[nums.length];//一定不偷第一家
dp1[0] = nums[0];
dp1[1] = Math.max(nums[0],nums[1]);
dp2[1] = nums[1];
dp2[2] = Math.max(nums[1],nums[2]);
for(int j = 2; j < nums.length-1; j ++){
dp1[j] = Math.max(dp1[j-2] + nums[j], dp1[j-1]);
}
for(int i = 3; i < nums.length; i ++){
dp2[i] = Math.max(dp2[i-2] + nums[i],dp2[i-1]);
}
return Math.max(dp1[nums.length-2],dp2[nums.length-1]);
}
}