你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
链接:https://leetcode-cn.com/problems/house-robber-ii
【思路】
很容易想到一种思路,只需要判断是否打劫当前房屋,若打劫,则相邻房屋自动排出不打劫;若当前房屋不打劫,那么相邻房屋是否打劫还未定。dp,我们考虑前n户房屋。可以想到dp方程:dp[i]=max(dp[i-1],dp[i-2]+nums[i]);但是这里需要注意的房屋排列是一个环,也就是第一户房屋和最后一户房屋也是相邻的,因此这里分两种情况考虑,1.若第一户人家被打劫,那么最后一户人家一定不能被打劫,也就是考虑除去最后一户人家的情况。2.若第一户人家不被打劫,那么考虑除去第一户人家的范围
int max(int a,int b){
return a>b?a:b;
}
int cal(int *nums,int start,int end){
int i,dp[end-start+2];
memset(dp,0,sizeof(dp));
for(i=start;i<=end;i++){
dp[i]=nums[i];
}
dp[start+1]=max(nums[start],nums[start+1]);
for(i=start+2;i<=end;i++){
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[end];
}
int rob(int* nums, int numsSize){
int i,j,tmp;
int dp[numsSize+1];
if(numsSize==0)
return 0;
if(numsSize==1)
return nums[0];
if(numsSize==2)
return max(nums[0],nums[1]);
//cal(nums,0,numsSize-2)第一户被打劫,cal(nums,1,numsSize-1)第一户不被打劫
return max(cal(nums,0,numsSize-2),cal(nums,1,numsSize-1));
}
此题也可以直接采用二维数组表示从i到j户人家的情况,这里使用i主要是为了判断第一户人家是否被打劫;若i==第一户&&j==最后一户,那么方程为dp[i][j]=max(dp[i][j-1],dp[i+1][j-2]+nums[j]);表示选择最后一户打劫时第一户不打劫。
int max(int a,int b){
return a>b?a:b;
}
int rob(int* nums, int numsSize){
int i,j;
int dp[numsSize+1][numsSize+1];
if(numsSize==0)
return 0;
if(numsSize==1)
return nums[0];
if(numsSize==2)
return max(nums[0],nums[1]);
memset(dp,0,sizeof(dp));
for(i=0;i<numsSize;i++){
dp[i][i]=nums[i];
}
for(i=0;i<numsSize-1;i++){//后面j-2会用到,提前计算
dp[i][i+1]=max(nums[i],nums[i+1]);
}
dp[0][1]=max(nums[0],nums[1]);
for(j=2;j<numsSize;j++){
for(i=j-1;i>=0;i--){
if(i==0&&j==numsSize-1)
dp[i][j]=max(dp[i][j-1],dp[i+1][j-2]+nums[j]);
else
dp[i][j]=max(dp[i][j-1],dp[i][j-2]+nums[j]);
}
}
return dp[0][numsSize-1];
}