很显然,这类题目是用动态规划来做,因为偷不偷这间房子会对偷不偷下一间房子产生影响,显然这是个递推的过程,那么我们接下来就是确定dp数组含义和递推公式及初始化条件:
根据题目要求
dp[i] 数组的含义应该是前 i 间房子可以得到的最大金额
而每一间房子有偷和不偷两个选择
如果偷,那么前 i 间房子的得到的最大金额=前 i-2 间房子得到的最大金额+这间房子的金额, 即 dp[i]=dp[i-2]+nums[i]
如果不偷,那么前 i 间房子的得到的最大金额=前i-1间房子得到的最大金额, 即 dp[i]=dp[i-1]
显然两个中取较大值,则dp[i]=fmax(dp[i-1],dp[i-2]+nums[i])
初始条件也很简单,公式中的i-1>=0,i-2>=0,显然只有一间房子和只有两间房的情况要单独考虑,这里就不说了,直接上代码
int rob(int* nums, int numsSize){
if(numsSize==1)
return nums[0];
int f[numsSize];
f[0]=nums[0];
f[1]=fmax(nums[0],nums[1]);
for(int i=2;i<numsSize;i++)
f[i]=fmax(f[i-2]+nums[i],f[i-1]);
return f[numsSize-1];
}
当然,这题的空间复杂度是可以优化的,因为dp[i]只和dp[i-1]、dp[i-2]有关,我们只要用三个变量就能实现递推的效果,代码与下面的题目中的类似,这里就不重复写了
这题和上面那题一样,只是房子连接成了一个环状,即头尾两间房连在了一起,不能同时被盗窃,那么,我们只要去掉其中之一在计算,即符合题目条件,又去掉了环上的一点,使得环有了缺口,变相的转化成了上面那个题目,简化了问题
代码如下
int robrange(int*nums,int start,int end)
{
int first=nums[start],second=fmax(nums[start],nums[start+1]);
for(int i=start+2;i<=end;i++)
{
int third=fmax(second,first+nums[i]);
first=second;
second=third;
}
return second;
}
int rob(int* nums, int numsSize){
if(numsSize==1)
return nums[0];
if(numsSize==2)
return fmax(nums[0],nums[1]);
//在偷第一间不偷倒数第一间,不偷第一间偷倒数第一间中找到一个最大值
return fmax(robrange(nums,0,numsSize-2),robrange(nums,1,numsSize-1));
}
虽然,思路有了,代码也有了,但是很多人对这题还是有一个逻辑上的疑惑,就是它既然是一个环,那么它的每一个点都有可能是起始点,那么我们怎么保证上面代码中的情况,就一定会出现盗窃的最大金额呢???
其实问题很简单,我们宏观的把握一下这题,首先明确,这些房子中一定有某种盗窃顺序使得我们盗窃的金额最大,而第一间房和最后一间房不能被同时包含在这个盗窃选项中,因为这不符合题目的要求,那么我们只有三种可能---1.偷第一间房,不偷最后一间房,2.偷最后一间房,不偷第一间房,3.两间都不偷,而我们的robrange函数是在[start,end]区间内找到盗窃的最大金额,并没有明确说,偷不偷头尾两间房,所以第三种情况被包含在前两种情况中,故代码中只有两个值比较,
而上面的红字所说的问题,就在于你们对上面我所说的第一间房和最后一间房的含义理解上出现歧义,我所说的第一间房和最后一件房是在逻辑上的第一间房和最后一间房,即两者是环的开头和结尾就行,并不是非得是数组中首尾元素,也就是说,你将我上面的分析放在任意你选定的第一间房和最后一间房的位置上都说的通,而既然都可以得到最大偷窃金额,那么我们就将数组的首元素和尾元素当作第一间房和最后一间房,这里面的逻辑一定要搞清楚!!!