返回

LeetCode:198. 打家劫舍 动态规划的妙招

闲谈

前言

我们经常在生活和工作中遇到需要进行决策和规划的问题,这些问题往往具有以下特点:

  • 具有多个子问题
  • 存在重叠的子问题
  • 问题具有最优子结构,即问题的整体最优解依赖于其子问题的最优解

动态规划是一种解决这类问题的常见算法技术。其基本思想是将问题分解成较小的子问题,并通过子问题的最优解来逐步推导整个问题的最优解。

问题背景

现在我们面临一个经典的LeetCode问题——“打家劫舍”。这是一个有关小偷如何最大化偷窃金额的有趣问题。

假设您是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有不同的金额,影响您偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统。如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个非负整数数组,其中每个数字代表每间房屋中存放的金额,您的目标是计算出您在不触动警报装置的情况下,一夜之间能够偷窃的最大金额。

动态规划解决方案

我们将使用动态规划算法来解决这个问题。首先,我们定义一个数组dp,其中dp[i]表示从房屋0到房屋i(包括房屋i)能够偷窃的最大金额。

我们从dp[0]开始计算,dp[0]的值很显然是房屋0中的金额。对于dp[i](i>0),我们可以有两种选择:

  • 偷窃房屋i,此时我们可以获得房屋i中的金额,但我们需要舍弃房屋i-1中的金额。因此,dp[i] = dp[i-2] + nums[i]。
  • 不偷窃房屋i,此时我们可以获得dp[i-1]的最大金额。因此,dp[i] = max(dp[i-1], dp[i-2] + nums[i])。

最终,我们返回dp[n-1]的值,即从房屋0到房屋n-1能够偷窃的最大金额。

代码实现

以下是使用动态规划算法解决“打家劫舍”问题的Python代码实现:

def rob(nums):
    if not nums:
        return 0
    
    n = len(nums)
    dp = [0] * n
    
    dp[0] = nums[0]
    if n > 1:
        dp[1] = max(nums[0], nums[1])
    
    for i in range(2, n):
        dp[i] = max(dp[i-1], dp[i-2] + nums[i])
    
    return dp[n-1]

时间复杂度分析

动态规划算法的时间复杂度为O(n),其中n为数组的长度。这是因为我们只遍历数组一遍,计算每个子问题的最优解。

总结

在这个教程中,我们学习了如何使用动态规划算法来解决LeetCode上的“打家劫舍”问题。我们详细介绍了问题的背景、基本思路、代码实现以及时间复杂度的分析。通过这个教程,您掌握了一种动态规划算法的常见应用,并能将其拓展到其他类似问题中。