刷爆 LeetCode 周赛 337:进阶解题指南,助你制霸位掩码、回溯、同余、分桶和动态规划
2024-01-08 01:14:31
LeetCode 周赛 337 第三题:打家劫舍
简介
大家好,我是算法爱好者小彭。上周末举行的 LeetCode 第 337 场周赛,相信大家都有所耳闻。本场周赛虽然难度不高,但题目却极具巧思,考察了位掩码、回溯、同余、分桶和动态规划等多种算法技术。下面,我就带大家深入解析这道题目,领略算法之美。
题目解析:打家劫舍
题目给你一个整数数组 nums
,其中 nums[i]
表示第 i
户人家的钱数。你只能抢劫相邻的两家房屋,不能抢劫同一家的房屋两次。请你返回能抢劫的最高金额。
解题思路:动态规划
对于这道题目,我们可以采用动态规划的思想来求解。具体步骤如下:
- 定义状态: 设
dp[i]
为抢劫到第i
户人家的最高金额。 - 状态转移方程:
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
。其中,dp[i-1]
表示不抢劫第i
户人家,dp[i-2] + nums[i]
表示抢劫第i
户人家,且不抢劫第i-1
户人家。 - 初始条件:
dp[0] = nums[0]
,dp[1] = max(nums[0], nums[1])
。 - 求解: 根据状态转移方程,依次计算
dp[2]
、dp[3]
、...,直到dp[n]
,其中n
为数组nums
的长度。
代码实现:
def rob(nums):
if not nums:
return 0
dp = [0] * (len(nums) + 1)
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[len(nums) - 1]
复杂度分析:
- 时间复杂度:O(n),其中 n 为数组 nums 的长度。
- 空间复杂度:O(n)。
扩展:贪心算法
除了动态规划,我们还可以使用贪心算法来求解这道题目。贪心算法的思路是:在每一步,选择当前能获得最大收益的操作。
具体步骤如下:
- 从第 0 户人家开始。
- 如果当前人家的钱数大于下一户人家的钱数,则抢劫当前人家。
- 否则,跳过当前人家,抢劫下一户人家。
- 重复步骤 2 和 3,直到抢劫完所有人家。
代码实现:
def rob_greedy(nums):
if not nums:
return 0
money = 0
prev = 0
for num in nums:
if num > prev:
money += num
prev = num
return money
复杂度分析:
- 时间复杂度:O(n),其中 n 为数组 nums 的长度。
- 空间复杂度:O(1)。
总结
通过对 LeetCode 周赛 337 第三题的深入解析,我们掌握了位掩码、回溯、同余、分桶和动态规划等多种算法技术。动态规划是一种求解最优子结构问题的强大技术,而贪心算法是一种简单高效的求解近似最优解的方法。希望这篇文章能帮助大家提升算法思维,在未来的编程竞赛中取得佳绩。
常见问题解答
1. 为什么动态规划比贪心算法更优?
动态规划可以保证找到全局最优解,而贪心算法只能保证局部最优解。在某些情况下,贪心算法可能无法找到全局最优解。
2. 这道题目的扩展可以有哪些?
这道题目可以有多种扩展,例如:
- 允许抢劫任意相邻或不相邻的房屋。
- 抢劫的房屋存在冷却时间,抢劫后的一段时间内不能再次抢劫。
- 抢劫的房屋存在不同的收益和风险。
3. 动态规划中的状态转移方程是如何推导出来的?
状态转移方程是基于题目的约束条件和定义的。对于这道题目,dp[i]
表示抢劫到第 i
户人家的最高金额,而dp[i-1]
表示不抢劫第 i
户人家,dp[i-2] + nums[i]
表示抢劫第 i
户人家,且不抢劫第 i-1
户人家。因此,状态转移方程为 dp[i] = max(dp[i-1], dp[i-2] + nums[i])
。
4. 贪心算法中,如何判断是否应该抢劫当前人家?
贪心算法中,如果当前人家的钱数大于下一户人家的钱数,则应该抢劫当前人家。这是因为,如果不抢劫当前人家,则下一户人家的钱数会减少,而抢劫当前人家的钱数会增加。
5. 动态规划和贪心算法的优缺点是什么?
- 动态规划:优点是能保证找到全局最优解,缺点是时间复杂度和空间复杂度较高。
- 贪心算法:优点是时间复杂度和空间复杂度较低,缺点是不能保证找到全局最优解。