leetcode之路(198.打家劫舍 and 213.打家劫舍ii)
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 。
“实现一个功能如果需要10分钟,思考的时间是7分钟”
思考过程:
题意很简单,意思就是说给定一个非负整数数组,在所取的数不能相邻的前提下去数组中的数,求这些数的和最大为多少。向这种求“最大”、“最小”的题目在很多情况下要用到动态规划的方法。
看到不连续我们首先想到的应该是下图中的前两种情况,至于第三种情况有没有可能是题目的答案呢?当然有可能,比如:数组 [100,1,1,100,1,100,1,100,1,1,100,1,1,100];那么第四种情况可能是题目的解吗?
哈哈哈,当然不可能,间隔三个或更多个数字,其中就跳过了可以取的数字(比如图中的第三个数字),只会使我们取得数字的值变小(为0时不变,反正不会变大)。
我们可以从左到右遍历一遍数组,同时定义一个数组dp来记录到nums[i]时,我们可以取到的最大的和(一定要理解这句话,dp[i]所存放的值是从nums[0]到nums[i]可以取到最大的值);这样我们的问题就转换成了到第i个数字取不取的问题,取的话就是dp[i]=dp[i-2]+nums[i]
(取nums[i]则不能取nums[i-1],dp[i-2]就是当前含nums[i-1]的最大和),不取就是dp[i]=dp[i-1]
(dp[i-1]就是当前不含nums[i]的最大和)。特殊情况:当i=0时,我们可以取到的最大的值应该是nums[0],所以dp[0]=nums[0]
,当i=1时,我们可以取到最大的值是数组中前两个数的最大值,所以dp[1]=max(dp[0],nums[1])
;
附上代码:
#include<vector>
using namespace std;
int dp[100];
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==0)
return 0;
for(int index=0;index<nums.size();index++){
if(index==0) dp[index]=nums[0];
else if(index==1) dp[index]=max(nums[0],nums[1]);
else dp[index]=max(nums[index]+dp[index-2],dp[index-1]);
}
return dp[nums.size()-1];
}
};
√ Accepted
√ 69/69 cases passed (0 ms)
√ Your runtime beats 100 % of cpp submissions
√ Your memory usage beats 75.24 % of cpp submissions (8.6 MB)
213打家劫舍ii 题目
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
题意 这题相对上一题的变化时,房屋成环,也就是“取头不取尾,取尾不取头”,尽然这样,那我们就想办法把这个环给“断开”,我们可以仍然使用上题中的代码,**我们从nums[0]到nums[nums.size()-2]计算一次(“有头的情况”),再从nums1[1]到nums[nums.size()-1]计算一次(“有尾的情况”)。**这么一来数组的头和尾一定不会同时取到,我们取两种情况的最最大值就是答案了。
附上代码:
class Solution {
public:
int dp[100];
//拿头不拿尾,房屋成环,
//人工断环,nums去掉第一个数字,算一次,再去掉最后一个数字算一次,取大的即所求
int rob(vector<int>& nums) {
if(nums.size()==0)
return 0;
if(nums.size()==1)
return nums[0];
int ans1,ans2;
for(int index=0;index<nums.size()-1;index++){
if(index==0) dp[index]=nums[0];
else if(index==1) dp[index]=max(nums[0],nums[1]);
else dp[index]=max(nums[index]+dp[index-2],dp[index-1]);
}
ans1=dp[nums.size()-2];
for(int index=1;index<nums.size();index++){
if(index==1) dp[index]=nums[1];
else if(index==2) dp[index]=max(nums[1],nums[2]);
else dp[index]=max(nums[index]+dp[index-2],dp[index-1]);
}
ans2=dp[nums.size()-1];
return ans1>=ans2?ans1:ans2;
}
};
提交结果
√ Accepted
√ 74/74 cases passed (0 ms)
√ Your runtime beats 100 % of cpp submissions
√ Your memory usage beats 88.58 % of cpp submissions (8.5 MB)