引言
这是本人对《剑指offer》89题房屋偷盗问题的一些总结,之前还总结过爬楼梯问题,建议先看一下爬楼梯问题的总结再看这篇文章。
先上题目
解决问题前先写状态方程
- f(x) 表示小偷从标号为 0 的房屋开始偷最多能偷取到的财物的最大值
- f(0) = 2 只有一个房子直接偷就完了
- f(1) = max(nums[0], nums[1]) // 两个房子就偷钱多的那个
- f(x>=2) = max(f(x-1), f(x-2) + nums[x]) // 比对不偷第 x 个房子和偷第 x 个房子那个比较大
- 偷x-1时,x房子肯定是不能偷的,因为个题目说了相邻房屋会叫来黑猫警长,所以没有 + nums[x]
- 但是并不是说 f(x-1) 就一定偷了 x-1 的房子,它也是包含两种情况的
解法一
// 自上而下写法,递归
const nums = [2, 3, 4, 5, 3]
function getMax(nums) {
// 用 dep 存放 f(x) 的结果即 dep[x] = f(x)
const dep = []
helper(nums, nums.length - 1)
// 给 dep[i] 赋值
function helper(nums, i) {
if (i === 0) {
dep[0] = nums[0]
} else if (i === 1) {
dep[1] = Math.max(nums[0], nums[1])
} else { // 这个地方可以改成 else if(!dep[i]) 因为赋值过的我们就没必要再赋值了
helper(nums, i - 1) // 给 dep[i - 1] 赋值
helper(nums, i - 2) // 给 dep[i - 2] 赋值
dep[i] = Math.max(dep[i - 1], dep[i - 2] + nums[i])
}
}
return dep[nums.length - 1]
}
console.log(getMax(nums));
解法二
// 自下而上写法,缓存 + 空间复杂度优化,这个地方看不懂的同学可以看看我发表的爬楼梯文章,秒懂!
function getMax(nums) {
// 用 dep 来存 f(x) 的结果
const dep = [nums[0]] // dep[0] 赋值
dep.push(Math.max(nums[0], nums[1])) // dep[1] 赋值
for (let i = 2; i < nums.length; i++) {
// 求 f(X) 的值只需要用到 f(x-1)、f(x-2)、nums[x] 的值所以并不需要把所有的结果都完整保存
dep[i%2] = Math.max(dep[(i-2)%2] + nums[i], dep[(i-1)%2])
}
return dep[(nums.length - 1) % 2]
}
利用双状态方程来求解
- f(x) 表示不偷盗 x 房屋最多能偷盗的财物, g(x) 表示偷盗 x 房屋最多能偷盗的财物
- f(0) = 0 g(0) = 2
- f(1) = 2 g(1) = 3
- f(x) = max(g(x-1), f(x-1)) g(x) = f(x-1) + nums[x]
function getMax(nums) {
const dep = [
[0, 2], // 存 f(x)
[2, 3] // 存 g(x)
]
// 这个地方一样也可以做空间复杂度优化,因为逻辑比起上面的相对比较复杂我们先理清思路
for(let i = 2; i < nums.length; i++) {
dep[0][i] = Math.max(dep[1][i-1], dep[0][i-1])
dep[1][i] = dep[0][i-1] + nums[i]
}
return Math.max(dep[0][nums.length - 1], dep[1][nums.length - 1])
}
优化
// 空间复杂度优化版本
function getMax(nums) {
const dep = [
[0, 2], // 存 f(x)
[2, 3] // 存 g(x)
]
// f(x) = max(g(x-1), f(x-1)) g(x) = f(x-1) + nums[x]
// 和第二种解法的优化原理是一样的
for(let i = 2; i < nums.length; i++) {
dep[0][i%2] = Math.max(dep[1][(i-1)%2], dep[0][(i-1)%2])
dep[1][i%2] = dep[0][(i-1)%2] + nums[i]
}
return Math.max(dep[0][(nums.length - 1)%2], dep[1][(nums.length - 1)%2])
}
后话
今天这篇产出确实有点不容易,最近项目要推新版,在学习微前端还有对项目进行架构,但是上星期在爬楼梯问题立了flag怎么说也想把它完成,加上离秋招也越来越近了,确实需要去多刷一些题,继续立个flag,下周仍然会有一篇动态规划题目的总结!如果对大家有帮助的话就给个三连吧!2022.5.16 00:13 上海浦东