一.House Robber I 的题意是:有一排商铺,强盗不能连续抢劫相邻的两家,否则会触动报警器,求强盗在不触动报警器的前提下,可以抢劫的最多金钱总和。(输入一维数组,从中选择若干个数,使得这些数的和最大,并且这些数互不相邻):
本题可以用两种方法解决:自顶向下(递归,直观)和自底向上(递推,动态规划的思想,时复低于前者),先介绍递归方法,我们可以认为有n个商铺,走到第m家商铺面,能获得的最大金额总和为两种情况中的最大值:Math.max(nums[m]+solve(m-2),solve(m-1)),所以代码如下:
public static int rob1(int[] nums) {
return solve(nums.length-1,nums);
}
private static int solve(int index, int[] nums) {
if(index <0 ){
return 0;
}
int max = Math.max(nums[index]+solve(index-2,nums), solve(index-1,nums));
return max;
}
这种方法非常耗时,因为有很多冗余计算(画个图便可以看出),所以改用第二中方法,动态规划。我们开一个数组,记录那些已经算过的子问题的解,这样就只需查数组不需要计算多次,所以将递归改为递推,递推公式基本不变,代码如下:
public class Solution {
private int[] result;
public int rob(int[] nums) {
if(nums.length<=0){
return 0;
}
if(nums.length==1){
return nums[0];
}
int length = nums.length;
result = new int[length]; //result数据用于记录子问题的解,然后递推
result[0] = nums[0]; //以下两行定义边界情况
result[1] = Math.max(nums[0],nums[1]);
for(int i=2;i<length;i++){ //开始递推,递推公式没变
result[i] = Math.max(nums[i]+result[i-2],result[i-1]);
}
return result[length-1];
}
}
二.
House Robber II的题意是:这些商铺不是一排了,而是围成了一个圈,这样以前数组的第一个元素与最后一个元素也变成相邻的,其他要求不变,仍然是找出若干个不相邻的数,使他们的和最大。
思路:这道题在上一道的基础上加了限制,即抢了第一家就不能抢最后一家,抢了最后一家就不能抢第一家。所以分为两种情况分别进行计算,最后返回这两种情况中结果最大一个即为所求,我们这次要开两个数组,分别记录这两种情况的子问题的题,其他地方思路不变(边界的定义,递推公式均不变,只是数组为两个不同的数组而已),代码如下:
public class Solution {
public int rob(int[] nums) {
if(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-1]; //记录第1个到倒数第2个
int[] dp2 = new int[nums.length-1]; //记录第2个到倒数第1个
dp1[0] = nums[0];
dp1[1] = Math.max(nums[0], nums[1]);
dp2[0] = nums[1];
dp2[1] = Math.max(nums[1], nums[2]);
for(int i=2;i<nums.length-1;i++){
dp1[i] = Math.max(nums[i]+dp1[i-2], dp1[i-1]);
dp2[i] = Math.max(nums[i+1]+dp2[i-2], dp2[i-1]);
}
return Math.max(dp1[nums.length-2], dp2[nums.length-2]);
}
}