题目:213.House Robber II
题目链接
https://leetcode.com/problems/house-robber-ii/description/
题目描述
你是一个专业的强盗,计划抢劫沿街的房屋。每间房都藏有一定的现金,阻止你抢劫他们的唯一的制约因素就是相邻的房屋有保安系统连接,如果两间相邻的房屋在同一晚上被闯入,它会自动联系警方。
给定一个代表每个房屋的金额的非负整数列表,确定你可以在没有提醒警方的情况下抢劫的最高金额。
在上次盗窃完一条街道之后,窃贼又转到了一个新的地方,这样他就不会引起太多注意。这一次,这个地方的所有房屋都围成一圈。这意味着第一个房子是最后一个是紧挨着的。同时,这些房屋的安全系统与上次那条街道的安全系统保持一致。
给出一份代表每个房屋存放钱数的非负整数列表,确定你可以在不触动警报的情况下盗取的最高金额。
解题思路
这是一道动态规划问题,将房子编号为1~n,设M[i]为盗贼到第i间房子时,盗取的最高金额,设V[i]表示第i间房子的金额。
盗贼一定会偷尽可能多的房屋,即当他偷到第n-4间房子时,他不会就此罢手,而一定会去偷第n-2、n-1间房子。
他也不会直接从第n-4间房子直接到第n间房子偷窃,因为这就少偷了第n-2间房子。
基于上述判断,我们可以断言,当盗贼偷窃第i间房子时,他一定是从第i-2或第i-3间房子过来的,即:
M[i] = max(M[i-2], M[i-3]) + V[i]
接下来考虑边界条件:
如果房子不是围成一圈(即第1间房子和第n间房子不相邻),那么盗贼盗取的最高金额必为max(M[n-1], M[n]),且为了最大收益,盗贼第一次盗取的房子一定是第1间或第2间。
如果房子围成一圈,我们仍可以用遍历的方式求解,当盗贼盗取了最高金额的前提下,还需要考虑以下情况:
1、若盗贼最后盗取的房子为第n间,那么他第一次盗取的房子一定不是第1间
2、若盗贼最后盗取的房子为第n间,那么他第一次盗取的房子可能是第3间
3、若盗贼第一次盗取的房子是第1间,那么他最后盗取的房子可能是第n-2间
用M[i][j]表示盗贼第一次盗取的房子为第i间时(1<=i<=3),盗取第j间房子能得到的最大收益,则盗贼盗取的最大金额必为:
M[2][n], M[3][n], M[1][n-1], M[2][n-1], M[1][n-2]中的最大值
代码
int x = [](){
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}();
class Solution {
public:
int pre(vector<int> arr[],int j,int i){
if(i-3<0)return arr[j][i-2];
return max(arr[j][i-2],arr[j][i-3]);
}
int rob(vector<int>& nums) {
if(!nums.size())return 0;
vector<int> arr[3]={vector<int>(nums.size(),0),vector<int>(nums.size(),0),vector<int>(nums.size(),0)};
arr[0][0]=nums[0];
if(nums.size()>1)arr[1][1]=nums[1];
if(nums.size()>2)arr[2][2]=nums[2];
for(int i=2;i<nums.size();++i){
for(int j=0;j<3;++j){
arr[j][i]=pre(arr,j,i)+nums[i];
}
}
if(nums.size()<2)return arr[0][0];
int maxn=max(arr[1][nums.size()-1],max(arr[0][nums.size()-2],arr[1][nums.size()-2]));
if(nums.size()<3)return maxn;
return max(maxn,max(arr[2][nums.size()-1],max(arr[1][nums.size()-3],arr[0][nums.size()-3]