动态规划之房屋偷盗问题,不仅要写对还要会缓存和空间优化!

510 阅读3分钟

引言

这是本人对《剑指offer》89题房屋偷盗问题的一些总结,之前还总结过爬楼梯问题,建议先看一下爬楼梯问题的总结再看这篇文章。

先上题目

image.png

解决问题前先写状态方程

  • 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 上海浦东