打家劫舍 I
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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 。
【思路】:
这个动态规划简单。类似于背包的思想。
针对每一个物品,只有拿和不拿两种情况。存在大量子问题,然后建立递归式
dp[n] = Math.max(dp[n - 1], nums[n] + dp[n-2])
【代码】:
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
var dp = [];
dp.push(nums[0]);
for(let i = 1;i < nums.length;i++){
var val
if(i >= 2){
val = Math.max(nums[i] + dp[ i - 2], dp[ i -1]);
}else{
val = Math.max(nums[i], nums[i - 1]);
}
dp.push(val);
}
return dp[nums.length - 1];
};
打家劫舍 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 = [1,2,3]
输出:3
【思路】
这个题就是在上一个题的基础上加上一个限制,首尾不能同时偷。
这道题递归式确实更难想一些,需要借助第一问的代码。
我们举个例子:
6 6 4 8 4 3 3 10
这种情况我们的结果应为:
res(0, 7) = Math.max( fun(0, 6), nums[7] + fun(1,5) );
就是说,如果想从0—7号物品中取得最大值,那么参照第一问的思路,也是两种方案:
1、不拿第7号物品,那么我们只需要找到(0—6)号物品的最大值即可。
2、拿第7号物品,那么我们最终获得的价值为:7号价值 + (1–5)号物品的最大价值。(因为首尾不能同时拿)
而要实现fun(0,6),就是求数组 0—6部分的最大价值(用打家劫舍I 的代码即可)
【AC代码】:
var fun = function(start, end, nums){
if(start > end) return 0;
var dp = [];
dp.push(nums[start]);
for(let i = start + 1;i <= end;i++){
var val;
if(i >= start + 2){
val = Math.max(nums[i] + dp[ i - 2 - start], dp[ i-1 - start]);
}else{
val = Math.max(nums[i], nums[i - 1]);
}
dp.push(val);
}
return dp[end - start];
}
var rob = function(nums) {
let a = fun(0, nums.length - 2, nums);
let b = nums[nums.length - 1]+fun(1, nums.length - 3, nums);
return Math.max(a, b);
};