返回

动脑筋斗智斗勇,算法解题:打家劫舍

前端

在这场刺激的智力竞赛中,你必须成为一位技术高超的小偷,熟练运用动态规划算法,在不触发警报的情况下从一排房屋中窃取最大金额。准备好磨练你的算法技能了吗?让我们开始吧!

动态规划:巧妙解题之道

动态规划是一种强大的算法技术,特别适合解决涉及重叠子问题的优化问题。在打家劫舍问题中,相邻房屋的约束条件引入了重叠的子问题,而动态规划为我们提供了以高效的方式解决问题的理想方法。

算法步骤:

  1. 定义状态:dp[i] 表示考虑前 i 间房屋时能窃取到的最大金额。
  2. 状态转移方程: dp[i] = max(dp[i - 1], dp[i - 2] + loot[i])
    • dp[i - 1] 表示不窃取第 i 间房屋能获得的最大金额。
    • dp[i - 2] + loot[i] 表示窃取第 i 间房屋且不触发警报能获得的最大金额,因为相邻房屋不能同时被窃取。
  3. 边界条件: dp[0] = 0dp[1] = loot[1]

代码实现:

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

实例演示:

让我们用一个示例来说明算法的运作方式:

房屋金额:[2, 7, 9, 3, 1]

子问题分解:

  • 考虑前 1 间房屋:dp[1] = 2
  • 考虑前 2 间房屋:dp[2] = max(2, 7) = 7
  • 考虑前 3 间房屋:dp[3] = max(7, 2 + 9) = 11
  • 考虑前 4 间房屋:dp[4] = max(11, 7 + 3) = 14
  • 考虑前 5 间房屋:dp[5] = max(14, 11 + 1) = 15

因此,从这排房屋中能窃取到的最大金额为 15

优化技巧:

为了进一步优化算法,我们可以使用滚动数组来节省空间。滚动数组利用了状态转移方程中只依赖于前两个状态的特性。

def rob_optimized(nums):
    if not nums:
        return 0
    
    prev1 = 0
    prev2 = 0
    
    for num in nums:
        temp = max(prev2 + num, prev1)
        prev2 = prev1
        prev1 = temp
    
    return prev1

使用滚动数组后,算法的空间复杂度从 O(n) 降低到 O(1),其中 n 是房屋的数量。

结语:

打家劫舍问题完美诠释了动态规划算法的强大功能。通过将问题分解成较小的子问题并巧妙地使用状态转移方程,我们可以高效地找到最佳解决方案。无论你是一个经验丰富的程序员还是算法新手,我都鼓励你动手尝试一下,磨练你的算法技能。祝你在解题中收获乐趣和洞见!