LeetCode 198:纵横交错的屋顶,巧夺豪宅,看我如何攻克打家劫舍!
2024-01-25 04:48:44
巧用动态规划,破解打家劫舍谜题
导言
各位技术爱好者,欢迎踏入 LeetCode 198 的精彩世界,开启一场斗智斗勇的打家劫舍之旅。在这场智力博弈中,你的目标是巧妙地选择房屋,避开警报,尽可能搜刮更多战利品。
问题拆解
想象自己是一位身怀绝技的小偷,准备在一条沿街房屋中大显身手。每间房屋都藏匿着不同数量的现金,但狡猾的屋主设置了相互连通的防盗系统,一旦相邻房屋在同夜被盗,警报将立即响起。你的任务便是巧妙地选择房屋,避开防盗系统的耳目,尽可能地搜刮更多战利品。
动态规划:化繁为简
要解决这个难题,我们引入动态规划的思想。动态规划是一种将复杂问题分解成一系列较小、重叠子问题的技术。对于打家劫舍,我们可以将问题分解为子问题:对于房屋序列中的每一间房屋,我们有两个选择:
- 偷窃当前房屋,并跳过下一间房屋(以避免触发警报)
- 不偷窃当前房屋,直接跳到下一间房屋
算法流程:循序渐进
- 初始化: 创建两个数组,
dp
和prev_dp
,用于存储子问题的最优解。其中,dp[i]
表示偷窃到第i
间房屋的最大收益,prev_dp[i]
表示偷窃到第i-1
间房屋的最大收益。 - 递推: 对于每间房屋,计算两种选择下的收益:
- 选择偷窃:
dp[i] = prev_dp[i-2] + loot[i]
- 选择不偷窃:
dp[i] = prev_dp[i-1]
- 比较两种收益,选择更大的值更新
dp[i]
- 选择偷窃:
- 结果: 最后,返回
dp[n]
,其中n
为房屋总数,即偷窃到最后一家房屋的最大收益。
代码示例:Python
def rob(nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
if n == 0:
return 0
dp = [0] * n
prev_dp = [0] * n
for i in range(n):
if i >= 2:
dp[i] = max(prev_dp[i - 2] + nums[i], dp[i - 1])
elif i == 1:
dp[i] = max(nums[i], dp[i - 1])
else:
dp[i] = nums[i]
prev_dp[i] = dp[i - 1]
return dp[n - 1]
总结:后发制人
通过巧妙地运用动态规划,我们可以高效地解决打家劫舍问题。这个例子展示了动态规划在解决复杂问题中的强大作用,它可以将大问题分解成较小、易于解决的子问题,从而逐步推导出最优解。
常见问题解答
-
为什么动态规划能解决这个问题?
动态规划通过将问题分解成较小的、重叠的子问题,逐层递推,最终得到最优解。它适用于具有重叠子问题的优化问题,而打家劫舍问题正是这样一种问题。 -
dp
数组和prev_dp
数组有什么区别?
dp
数组存储着当前子问题的最优解,而prev_dp
数组存储着上一个子问题的最优解。这是动态规划中常用的技巧,用于保存之前计算的结果,避免重复计算。 -
如何选择偷窃或不偷窃当前房屋?
选择偷窃当前房屋,则可以获得当前房屋的收益,但需要跳过下一间房屋;选择不偷窃当前房屋,则可以直接跳到下一间房屋。我们比较两种选择下的收益,选择收益更大的一个。 -
为什么考虑两步之前的房屋(
prev_dp[i-2]
)?
防盗系统会触发警报,如果相邻房屋在同夜被盗。因此,偷窃当前房屋后,我们需要跳过下一间房屋,才能偷窃两步之后的房屋。 -
如何处理边界情况?
当房屋数量为 0 时,没有房屋可偷,因此返回 0。当房屋数量为 1 时,只有一个房屋可偷,因此直接返回这个房屋的收益。