问题
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
思路
主要是有环和打家劫舍1不同只用考虑相邻,即最后一个选与不选的最大值,有环说明头尾不能同时选,所以就选头不选尾和选尾不选头中选一个最大值出来!!!
且考虑前后都不相邻只用考虑第i个选了就不选i-1了,不选i就等于dp[i-1]这样就能够保证前后都不相邻了,虽然每次只考虑了前面,但考虑的都是目前的最后一个元素所以后面一定没有元素所以后面一定可以保证不相邻
若题目为只能偷前面相邻的不能偷后面相邻的,则这和偷的顺序就有关系了
则每次遍历就不是遍历前i个了而是全部n
如何才能保证第一间房屋和最后一间房屋不同时偷窃呢?如果偷窃了第一间房屋,则不能偷窃最后一间房屋,因此偷窃房屋的范围是第一间房屋到最后第二间房屋;如果偷窃了最后一间房屋,则不能偷窃第一间房屋,因此偷窃房屋的范围是第二间房屋到最后一间房屋。
假设数组 nums 的长度为 n。如果不偷窃最后一间房屋,则偷窃房屋的下标范围是 [0, n-2];如果不偷窃第一间房屋,则偷窃房屋的下标范围是 [1, n-1]。在确定偷窃房屋的下标范围之后,即可用第 198 题的方法解决。对于两段下标范围分别计算可以偷窃到的最高总金额,其中的最大值即为在 n 间房屋中可以偷窃到的最高总金额。
代码
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 1) return nums[0];
//与打家劫舍①相比不同的地方在于首尾相连,之前我们的函数只需要考虑遍历到最后就可以
//要想满足贪心,如果房子数>= 2,则必须选择nums[0], nums[1]其中一个,当已经选择nums[0]时我们不能选择nums[n - 1],同理当选择nums[1]时在满足条件情况下任选后面的元素
vector <int> dp1(n + 2, 0);//前面多填两个0
vector <int> dp2(n + 2, 0);
//选开始不选末尾
for(int i = 0; i < n - 1; ++i){
dp1[i + 2] = max(dp1[i + 1], dp1[i] + nums[i]);
}
//选末尾不选开始
for(int i = 1; i < n; ++i){
dp2[i + 2] = max(dp2[i + 1], dp2[i] + nums[i]);
}
return max(dp1[n], dp2[n + 1]);
}
};
// class Solution {
// public:
// int rob(vector<int>& nums) {
// int n=nums.size();
// vector<int> dp(n+1);
// dp[0]=nums[0];
// dp[1]=max(nums[0],nums[1]);
// int ans=0;
// //第一个选,最后一个不选的最大值
// for(int i=2;i<n-1;i++){
// dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
// }
// //最后一个选,第一个不选
// for(int j=n-1;j>)
// for(int )
// ans=dp[n-1];
// return ans;
// }
// };