返回

前端必刷题系列(90):[算法题解]动态规划之打家劫舍

前端

前言

动态规划是一种强大的算法范式,它通过将问题分解成更小的子问题并逐步解决这些子问题来解决复杂问题。它特别适用于解决具有重叠子问题的优化问题。在这一期的前端算法必刷题系列中,我们将使用动态规划来解决一道经典问题——“打家劫舍”。

问题

在一个小镇上,有 n 户相邻的房屋,每户房屋中都存放着一定金额的钱。一个窃贼计划在不惊动警报的情况下偷窃这些房屋。但是,有一个限制条件:相邻的房屋不能同时被偷窃。

给定一个整数数组 nums,其中 nums[i] 表示第 i 户房屋中存放的金额。请你计算窃贼能够偷窃到的最大金额。

动态规划解决方案

我们可以使用动态规划来解决这个问题。我们定义一个状态 dp[i],表示窃贼在考虑前 i 户房屋的情况下能够偷窃到的最大金额。

对于第 i 户房屋,窃贼有两种选择:

  1. 偷窃第 i 户房屋,则 dp[i] = nums[i] + dp[i - 2],因为相邻的房屋不能同时被偷窃。
  2. 不偷窃第 i 户房屋,则 dp[i] = dp[i - 1],因为窃贼只能考虑前 i - 1 户房屋。

因此,我们可以得到状态转移方程:

dp[i] = max(nums[i] + dp[i - 2], dp[i - 1])

代码实现

function rob(nums) {
  const n = nums.length;
  if (n === 0) return 0;
  const dp = new Array(n + 1).fill(0);
  dp[1] = nums[0];
  for (let i = 2; i <= n; i++) {
    dp[i] = Math.max(nums[i - 1] + dp[i - 2], dp[i - 1]);
  }
  return dp[n];
}

时间复杂度

动态规划算法的时间复杂度为 O(n),其中 n 是房屋的数量。

空间复杂度

动态规划算法的空间复杂度为 O(n),因为我们使用了额外的空间来存储状态 dp。

示例

输入:nums = [1, 2, 3, 1]
输出:4
解释:窃贼可以偷窃第 1 户和第 3 户房屋,总金额为 1 + 3 = 4。
输入:nums = [2, 7, 9, 3, 1]
输出:12
解释:窃贼可以偷窃第 1 户、第 3 户和第 5 户房屋,总金额为 2 + 9 + 1 = 12。

总结

在本文中,我们介绍了一种使用动态规划解决“打家劫舍”问题的算法。我们定义了一个状态 dp[i],表示窃贼在考虑前 i 户房屋的情况下能够偷窃到的最大金额。通过状态转移方程,我们可以逐步求解 dp[i]。该算法的时间复杂度和空间复杂度均为 O(n)。

动态规划是一种强大的算法范式,它可以有效解决具有重叠子问题的优化问题。通过将问题分解成更小的子问题并逐步解决这些子问题,我们可以找到问题的最优解。