题目:
198. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 400
首先我必须要提出怎么用递归来做(有点归并的思想),从下到上进行求值
但是会超时,因此后面采用动态规划来做
递归代码
class Solution {
private:
vector<int> m_nums;
int robMax(int index)
{
if(index>=m_nums.size())
{
return 0;
}
int ret1=0,ret2=0;
//这间偷,下一间房间只能不偷(跳过)
ret1=ret1+m_nums[index];
ret1=ret1+robMax(index+2);
ret2=ret2+robMax(index+1);
return max(ret1,ret2);
}
public:
int rob(vector<int>& nums)
{
m_nums=nums;
int index=0;
return robMax(index);
}
};
正确思路:
动态规划四要素
确认状态:dp[i] 表示前i间房屋能偷窃到的最高总金额,
初始化:dp[0]=nums[0] dp[1]=max(nums[0],nums[1])
状态转移方程: dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
返回值:dp[i-1]
class Solution {
public:
int rob(vector<int>& nums)
{
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
if(nums.size()==2) return max(nums[0],nums[1]);
//dp[i]代表着1 dp[i] 表示前i间房屋能偷窃到的最高总金额,
//状态转移方程:
//dp[i]=max(dp[i-1],dp[i-2]+nums[i])
vector<int> dp(nums.size(),0);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++)
{
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.size()-1];
}
};
优化:
但是,我们发现,我们申请了n个空间进行结果的保存,却只用到了连续的三个有效值,因此我们可以用有限个变量进行记录结果,时间复杂度是o(1)
代码
class Solution {
public:
int rob(vector<int>& nums)
{
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
if(nums.size()==2) return max(nums[0],nums[1]);
//dp[i]代表着1 dp[i] 表示前i间房屋能偷窃到的最高总金额,
//状态转移方程:
//dp[i]=max(dp[i-1],dp[i-2]+nums[i])
vector<int> dp(nums.size(),0);
int prepre=nums[0];
int pre=max(nums[0],nums[1]);
int retMax;
for(int i=2;i<nums.size();i++)
{
retMax=max(pre,prepre+nums[i]);
prepre=pre;
pre=retMax;
}
return retMax;
}
};
代码简写(这个是为了与打家劫舍2做铺垫)
我们发现我们需要将dp[0],dp[1]单调拎出来判断,有没有直接可以同一起来的,还真有,如下代码
class Solution {
public:
int rob(vector<int>& nums)
{
int prepre=0;
int pre=0;
int retMax=0;
for(int i=0;i<nums.size();i++)
{
retMax=max(prepre+nums[i],pre);
prepre=pre;
pre=retMax;
}
return retMax;
}
};
打家劫舍2
题目
213. 打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [0]
输出:0
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
思考:我们发现其实就是与打家劫舍1中1改了一点,就是第一个房屋,与最后一个房屋相连
如何将题目2变成题目1呢。
a.当index=0 这个元素不偷时 最后一个元素也就是索引为index=nums.size()-1 可以偷或者不偷 因此是rob2(nums,1,nums.size()-1)
b 当index=0 这个元素偷时 最后一个元素也就是索引为index=nums.size()-1 只能不偷 因此是nums[0]+rob2(nums,2,nums.size()-2)
求两者中最大即可
代码:
class Solution {
public:
int rob(vector<int>& nums)
{
//由于第一个和最后一个时联通的
if(nums.size()==1) return nums[0];
if(nums.size()==2) return max(nums[0],nums[1]);
//此时就是i=2开始了
int ret1=nums[0]+rob2(nums, 2,nums.size()-2);//第一个也就是index=0时偷时,我们发现最后一个nums.size()-1肯定不能偷了
int ret2=rob2(nums,1,nums.size()-1);
return max(ret1,ret2);
}
int rob2(vector<int>& nums,int start,int end)
{
int prepre=0;
int pre=0;
int retMax=0;
for(int i=start;i<=end;i++)
{
retMax=max(prepre+nums[i],pre);
prepre=pre;
pre=retMax;
}
return retMax;
}
};
新的一天,新的开始,加油,湖北二师