20250124-leetcode2944购买水果需要的最少金币数
给你一个 下标从 1 开始的 整数数组 prices ,其中 prices[i] 表示你购买第 i 个水果需要花费的金币数目。
水果超市有如下促销活动:
如果你花费 prices[i] 购买了下标为 i 的水果,那么你可以免费获得下标范围在 [i + 1, i + i] 的水果。
注意 ,即使你 可以 免费获得水果 j ,你仍然可以花费 prices[j] 个金币去购买它以获得它的奖励。
请你返回获得所有水果所需要的 最少 金币数。
示例 1:
输入:prices = [3,1,2]
输出:4
解释:
用 prices[0] = 3 个金币购买第 1 个水果,你可以免费获得第 2 个水果。
用 prices[1] = 1 个金币购买第 2 个水果,你可以免费获得第 3 个水果。
免费获得第 3 个水果。
请注意,即使您可以免费获得第 2 个水果作为购买第 1 个水果的奖励,但您购买它是为了获得其奖励,这是更优化的。
示例 2:
输入:prices = [1,10,1,1]
输出:2
解释:
用 prices[0] = 1 个金币购买第 1 个水果,你可以免费获得第 2 个水果。
免费获得第 2 个水果。
用 prices[2] = 1 个金币购买第 3 个水果,你可以免费获得第 4 个水果。
免费获得第 4 个水果。
示例 3:
输入:prices = [26,18,6,12,49,7,45,45]
输出:39
解释:
用 prices[0] = 26 个金币购买第 1 个水果,你可以免费获得第 2 个水果。
免费获得第 2 个水果。
用 prices[2] = 6 个金币购买第 3 个水果,你可以免费获得第 4,5,6(接下来的三个)水果。
免费获得第 4 个水果。
免费获得第 5 个水果。
用 prices[5] = 7 个金币购买第 6 个水果,你可以免费获得第 7 和 第 8 个水果。
免费获得第 7 个水果。
免费获得第 8 个水果。
请注意,即使您可以免费获得第 6 个水果作为购买第 3 个水果的奖励,但您购买它是为了获得其奖励,这是更优化的。
解题思路
问题分析
核心问题:
- 需要购买所有水果,找出最少的金币数
- 购买第i个水果后可以免费获得[i+1, i+i]范围内的水果
- 即使可以免费获得某个水果,仍然可以选择购买它来获得新的奖励范围
关键观察:
- 购买一个水果可能比免费获得它更优,因为购买后可以获得新的免费范围
- 需要在”免费获得”和”购买获得更多免费范围”之间做权衡
- 这是一个动态规划问题,因为当前决策会影响后续的选择
动态规划设计
状态定义:
dp[i]
表示获得从第i个水果到最后一个水果所需的最少金币数- i的范围是[1, n],因为题目说明下标从1开始
状态转移:
对于第i个水果,我们有两种选择:
a) 如果之前购买的水果能免费覆盖到第i个水果:- 可以选择免费获得它
- 也可以选择购买它来获得新的免费范围
b) 如果之前购买的水果不能免费覆盖到第i个水果: - 必须购买它
状态转移方程:
1
2
3
4dp[i] = min(
prices[i] + dp[next], # 购买当前水果
dp[i+1] # 如果可以免费获得,则跳过当前水果
)
代码实现
1 | class Solution: |
代码解释
记忆化搜索:
- 使用
@lru_cache
装饰器来缓存计算结果 - 避免重复计算相同状态
- 使用
状态参数:
i
:当前考虑的水果下标(从1开始)free_until
:之前购买能免费覆盖到的最远位置
决策过程:
- 如果当前水果在免费范围内,可以选择免费获得或购买
- 如果不在免费范围内,必须购买
- 购买后可以免费获得[i+1, i+i]范围内的水果
复杂度分析
时间复杂度:O(n²),其中n是水果的数量
- 对于每个位置i,我们需要考虑是否购买
- 每个状态最多被计算一次(因为有记忆化)
空间复杂度:O(n²)
- 主要是记忆化搜索的存储空间
- 状态数量是O(n²),因为有两个参数i和free_until