打家劫舍2
- 1. 题目解析
- 2. 讲解算法原理
- 3. 编写代码
1. 题目解析
题目地址:点这里
2. 讲解算法原理
-
首先,给定一个非负整数数组 nums,其中 nums[i] 表示第 i 家的财物价值。
-
定义两个辅助数组 f 和 g,长度都为 n(n 是数组 nums 的长度)。
- 数组 f 表示在偷盗范围为 [left, right] 内,且必须偷最后一家的情况下能够获取的最大财物价值。
- 数组 g 表示在偷盗范围为 [left, right] 内,且不偷最后一家的情况下能够获取的最大财物价值。
-
定义函数 rob_s,参数为 left、right 和 nums,表示在偷盗范围为 [left, right] 内计算可以获取的最大财物价值。
- 初始化数组 f 和 g 的第一个元素:
- f[left] = nums[left],表示在偷盗范围 [left, right] 内偷盗第一家,最大财物价值为第一家的价值。
- g[left] = 0,表示在偷盗范围 [left, right] 内不偷盗第一家,最大财物价值为0。
- 从 left+1 开始,从左到右遍历数组 nums,计算在偷盗范围 [left, right] 内的最大财物价值:
- 对于第 i 家,如果选择偷盗,则最大财物价值为前一家不偷盗的最大财物价值 g[i-1] 加上第 i 家的财物价值 nums[i],即 f[i] = g[i-1] + nums[i]。
- 对于第 i 家,如果选择不偷盗,则最大财物价值为前一家偷盗和不偷盗的最大财物价值中的较大值,即 g[i] = max(g[i-1], f[i-1])。
- 返回在偷盗范围 [left, right] 内的最大财物价值,即 max(f[right], g[right])。
- 在 rob 函数中,首先判断特殊情况:
- 如果数组 nums 的长度为1,则直接返回第一家的财物价值。
- 如果数组 nums 的长度为2,则返回两家财物价值中的较大值。
- 对于一般情况,分两种情况计算最大财物价值:
- 情况一:偷盗第一家,但不能偷盗最后一家。对范围 [2, n-2] 进行一次打家劫舍(使用函数 rob_s),再加上第一家的财物价值 nums[0],即 ret1 = rob_s(2, n-2, nums) + nums[0]。
- 情况二:不偷盗第一家,对范围 [1, n-1] 进行一次打家劫舍(使用函数 rob_s),即 ret2 = rob_s(1, n-1, nums)。
- 返回两种情况下的最大财物价值,即 max(ret1, ret2)。
3. 编写代码
class Solution {
public:
int rob_s(int left,int right,vector<int>& nums)
{
int n=nums.size();
vector<int> f(n);
vector<int> g(n);
f[left]=nums[left],g[left]=0;
for(int i=left+1;i<=right;i++)
{
f[i]=g[i-1]+nums[i];
g[i]=max(g[i-1],f[i-1]);
}
return max(f[right],g[right]);
}
int rob(vector<int>& nums) {
int n=nums.size();
//处理特殊情况
if(n==1) return nums[0];
else if(n==2) return max(nums[0],nums[1]);
vector<int> f(n);
vector<int> g(n);
//情况一:偷第一家,就不能偷最后一家->对[2,n-2]进行一次打家劫舍1再+nums[0]就行
int ret1=rob_s(2,n-2,nums)+nums[0];
//情况二:不偷第一家,对[1,n-1]进行一次打家劫舍1就行
int ret2=rob_s(1,n-1,nums);
return max(ret1,ret2);
}
};