你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有必定的现金,影响你偷窃的惟一制约因素就是相邻的房屋装有相互连通的防盗系统,若是两间相邻的房屋在同一夜被小偷闯入,系统会自动报警。算法
给定一个表明每一个房屋存放金额的非负整数数组,计算你 不触动警报装置的状况下 ,一晚上以内可以偷窃到的最高金额。数组
动态规划只能应用于有最优 子结构
的问题。最优子结构的意思是局部最优解能决定全局最优解
(对有些问题这个要求并不能彻底知足,故有时须要引入必定的近似)。markdown
简单地说,问题可以分解成子问题来解决
。spa
通俗一点来说,动态规划和其它遍历算法(如深/广度优先搜索)都是将原问题拆成多个子问题而后求解
,他们之间最本质的区别是,动态规划保存子问题的解,避免重复计算
。3d
解决动态规划问题的关键是找到状态转移方程
,这样咱们能够通计算和储存子问题的解来求解最终问题
。code
同时,咱们也能够对动态规划进行空间压缩
,起到节省空间消耗的效果。orm
在一些状况下,动态规划能够当作是带有状态记录(memoization)的优先搜索
。it
动态规划是自下而上的
,即先解决子问题,再解决父问题;io
而用带有状态记录的优先搜索
是自上而下
的,即从父问题搜索到子问题,若重复搜索到同一个子问题则进行状态记录,防止重复计算。table
若是题目需求的是最终状态,那么使用动态搜索比较方便;
若是题目须要输出全部的路径,那么使用带有状态记录的优先搜索会比较方便。
定义数组 dp,dp[i]到表示第 i 个房子时,能够抢劫的最大数量。
这时候有两种选择,一不抢劫这个房子,累计金额为dp[i-1];二抢,累计的最大金额dp[i-2],由于咱们不
可以抢劫第 i-1 个房子,不然会触发警报机关,状态转移方程为 dp[i] = max(dp[i-1],
nums[i] + dp[i-2])。
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
if(!nums.length) return 0;
if(nums.length === 1) return nums[0]
if(nums.length === 2) return Math.max(nums[0], nums[1]);
let n = nums.length;
let dp = [];
dp[0] = nums[0];
dp[1] = Math.max(Math.max(nums[0], nums[1]));
for(let i = 2; i < n; i++) {
dp[i] = Math.max(dp[i-1], nums[i] + dp[i-2]);
}
return dp[n-1];
};
复制代码
var rob = function(nums) {
if(!nums.length) return 0;
if(nums.length === 1) return nums[0]
if(nums.length === 2) return Math.max(nums[0], nums[1]);
let n = nums.length;
// O(n)--> O(1)
let pre2 = nums[0], pre1 = Math.max(Math.max(nums[0], nums[1])), cur;
for(let i = 2; i < n; i++) {
cur = Math.max(pre1, nums[i] + pre2);
pre2 = pre1;
pre1 = cur;
}
return cur;
};
复制代码