一、题意
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
二、分析和解答
public int rob(int[] nums) {
int s1 = 0;//not rob
int s2 = 0;//rob
for(int i=0;i<nums.length;i++){
//tmp保存不抢上一家的值
int tmp = s1;
//不抢这一家,等于 抢上一家和不抢上一家的较大值
s1 = Math.max(s1,s2);//not rob
//抢这一家, 等于 不抢上一家 和 这一家的和
s2 = tmp + nums[i];
}
return Math.max(s1,s2);
}
不能抢劫连着的两家。更通俗一点:求数组中不连续数字的最大的和。
这里假设S[i]是抢劫到第i家的时候钱的总数,那么
S[i] = nums[i] + S[i-2] (抢第i家家) 或者是S[i] = S[i-1](不抢第i家)
int search(int id,int[] nums,int[] sum){
if(id < 0)
return 0;
return Math.max(nums[id] + search(id-2,nums),search(id-1,nums));
}
public int rob(int[] nums) {
return search(nums.length-1,nums);
}
提交后发现,时间超时!从代码可以看到,每一个递归里面有两个子递归,最后导致整个函数的时间复杂度达到了2^n左右。
2、优化
冗余的部分在于:之前已经记录过的数据没有被利用,所以这里申请一个数组来记录之前运算过的数据!
public int rob(int[] nums) {
int len = nums.length;
if(len == 0)
return 0;
if(len == 1)
return nums[0];
if(len == 2)
return Math.max(nums[0],nums[1]);
int[] sum = new int[nums.length];
sum[0] = nums[0];
sum[1] = Math.max(nums[0],nums[1]);
//sum[2] = Math.max(nums[0] + nums[2],nums[1]);
int i = 2;//3;
while(i < len){
sum[i] = Math.max(sum[i-1],nums[i] + sum[i-2]);
i++;
}
return sum[i-1];
}
3、再优化
后来发现,我们只需要最后的sum[i]这一个数据,因此是没有必要申请一个数组,仅仅需要两个变量即可,一个记录不抢当前这家所获得的总利润(其实就是抢劫上一家和不抢劫上一家利润的最大值,因为如此,这家得到的利润就是0,还是上一家的数据,但肯定是较大者),另一个记录抢劫这家所获得的总利润(从上一家得到的最大值加上这家的数据)。
public int rob(int[] nums) {
int s1 = 0;//not rob
int s2 = 0;//rob
for(int i=0;i<nums.length;i++){
int tmp = s1;
s1 = Math.max(s1,s2);//not rob
s2 = tmp + nums[i];
}
return Math.max(s1,s2);
}
Note:抢劫上一家(前一家不能rob)不一定会比 不抢劫上一家(前一家可以rob)的值大,因为前一家有影响啊!