打家劫舍
动态规划的又一道基础题,先分析问题子结构,再列公式。
问题描述
小偷进行偷窃,不能偷窃相邻的两个房间,不然会触发报警,请问怎样才能窃取到最大的金额。
金额数字用数组表示
[1, 2, 3, 1]
问题分析
-
要首先看一个问题能不能由他的子问题进行解决
- 可以:能用动态规划;不可以:不能用动态规划解决。
-
假设一共有n个房间,求偷n个房间的最大值
-
最后一个房间只有偷和不偷两种方式
- 偷:f(n) = f(n-2) + n
- 不偷: f(n) = f(n-1)
-
所以,n个房间的最大值
- f(n) = max(f(n-2) + n, f(n-1))
-
基本思想如上所述,该问题可以由动态规划算法完成。
let rob = function(nums) {
let len = nums.length;
if(len === 0)
return 0;
if(len === 1)
return nums[0];
if(len === 2)
return Math.max(nums[0], nums[1]);
let dp = []; // 用数组来记录每到k个房间的最大值
dp[0] = 0;
dp[1] = nums[0];
dp[2] = Math.max(nums[0], nums[1])
for(let i = 3; i <= len; i++) {
dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]); // 用刚才推算出来的算法来进行计算
}
return dp[len]; //返回最后的值
}
仔细观察可以发现,算到n个房间只需要n-1和n-2的最大值就可以了,也就是我们只需要保存前面两个情况的最大值。
就可以算出n个房间的最大值,所以我们只需要利用变量重复赋值就可以了,不需要用到数组。
这样我们的空间复杂度就由O(N)变成了O(1)。
let rob = function(nums) {
let len = nums.length;
if(len === 0)
return 0;
if(len === 1)
return nums[0];
if(len === 2)
return Math.max(nums[0], nums[1]);
// temp记录每次到i的结果
let temp;
// 利用prev2记录i-2的最大值,prev1记录i-1的最大值。
let prev2 = nums[0];
let prev1 = Math.max(nums[0], nums[1])
for(let i = 3; i <= len; i++) {
temp = Math.max(prev1, prev2 + nums[i-1]);
prev2 = prev1;
prev1 = temp;
}
return temp;
}