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 。
分析
1、首先想一想如果是暴力如何做?
假设从最后一家店铺开始抢,那么只会遇到2种情况,即:抢这家店和下下家店,或者不抢这家店。所以我们得到递归的公式:
Math.max(solve(nums,index-1),solve(nums,index-2)+nums[index]);
执行结果如下图,我们看到递归有很多重复计算。我们可以用一个数组来记录已经计算过的结果,再次碰到的时候直接返回。
实现1 - 暴力递归
var rob = function(nums) { return solve(nums,nums.length-1); }; function solve(nums,index){ if(index<0) return 0 ; return Math.max(solve(nums,index-1),solve(nums,index-2)+nums[index]); }
实现2 - 递归去冗余,记忆化搜索
var rob = function(nums) { //用ret来保存结果,初始化为-1 this.ret=[]; for(var i=0 ;i<nums.length;i++){ this.ret.push(-1) } return solve(nums,nums.length-1); }; function solve(nums,index){ if(index<0) return 0 ; //因为初始化-1,所以有解的结果就直接返回。 if(this.ret[index]>=0) return this.ret[index]; //每次递归把当前的结果保存到ret中 this.ret[index]=Math.max(solve(nums,index-1),solve(nums,index-2)+nums[index]); //返回每一次Math.max(...)的结果 return this.ret[index]; }
实现3 - 动态规划
/** 1.dp[i]表示当前状态的最优解,即打劫到第i家时获得的最高金额。 2.每个状态有两种情况。 ①打劫第i家:dp[i-2] + nums[i],表示根据上上家的最优解(最高价值)计算得到当前最优解。 ②不打劫第i家:dp[i-1],表示任然延续上一个状态,也就是延续上一个最优解。 3.取①和②的最大值作为当前最优解。 */ var rob = function(nums) { var len=nums.length; if(len===0) return 0; var dp = new Array(len); dp[0]=nums[0]; dp[1]=Math.max(nums[0],nums[1]); for(var i=2;i<len;i++){ dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]); } return dp[len-1]; };
运算过程如下:
参考:https://www.jianshu.com/p/77f1e8157277