leetcode
leetcode 2501 ~ 2550
购买水果需要的最少金币数

购买水果需要的最少金币数

难度:

标签:

题目描述

You are at a fruit market with different types of exotic fruits on display.

You are given a 1-indexed array prices, where prices[i] denotes the number of coins needed to purchase the ith fruit.

The fruit market has the following offer:

  • If you purchase the ith fruit at prices[i] coins, you can get the next i fruits for free.

Note that even if you can take fruit j for free, you can still purchase it for prices[j] coins to receive a new offer.

Return the minimum number of coins needed to acquire all the fruits.

 

Example 1:

Input: prices = [3,1,2]
Output: 4
Explanation: You can acquire the fruits as follows:
- Purchase the 1st fruit with 3 coins, you are allowed to take the 2nd fruit for free.
- Purchase the 2nd fruit with 1 coin, you are allowed to take the 3rd fruit for free.
- Take the 3rd fruit for free.
Note that even though you were allowed to take the 2nd fruit for free, you purchased it because it is more optimal.
It can be proven that 4 is the minimum number of coins needed to acquire all the fruits.

Example 2:

Input: prices = [1,10,1,1]
Output: 2
Explanation: You can acquire the fruits as follows:
- Purchase the 1st fruit with 1 coin, you are allowed to take the 2nd fruit for free.
- Take the 2nd fruit for free.
- Purchase the 3rd fruit for 1 coin, you are allowed to take the 4th fruit for free.
- Take the 4th fruit for free.
It can be proven that 2 is the minimum number of coins needed to acquire all the fruits.

 

Constraints:

  • 1 <= prices.length <= 1000
  • 1 <= prices[i] <= 105

代码结果

运行时间: 39 ms, 内存: 16.3 MB


解释

方法:

此题解采用了动态规划的思想,使用一个双端队列 (deque) 优化状态的转移过程。基本思路是从数组的末尾向前计算每个位置购买水果的最小花费。对于每个位置i,算法计算购买该水果并获取后续免费水果的总成本。双端队列用于存储可能的最小成本和对应的位置,从而快速获得后续位置的最小花费。队列中维护的是一系列(位置,花费)对,确保队列始终保持递增的花费顺序。如果当前计算的花费比队列头部的花费还低,那么头部的花费将不再有用,因此会被移除。每次计算都是基于队列中花费最小的位置来决定当前位置的最优决策。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在双端队列中,为什么选择移除队尾元素当`q[-1][0] > i * 2 + 1`,这个条件是如何确定的?
在此动态规划解法中,每次购买水果都可能使得接下来的几个水果是免费的,具体到此题,购买位置i的水果时,可以免费获得位置i+1到2i的水果。因此,对于每个位置i,只需要考虑从i到2i位置之后的最小花费。队列`q`中存储的元组`(j, cost)`中,j代表最远可以免费到达的位置,cost是从该位置开始的最小花费。当`q[-1][0] > i * 2 + 1`时,意味着队列尾部的元素代表的状态超出了当前位置i免费范围的最远边界,因此不会影响到i位置的决策,可以被安全移除。
🦆
你如何保证在移除队列头部元素时,不会错过可能的最小花费?请解释`while f <= q[0][1]`这一行代码的逻辑和必要性。
在计算当前位置i购买水果的总成本f时(包括购买i的花费和从i位置之后的最小花费),需要确保队列头部始终是可达范围内的最小花费。如果计算得出的新成本f小于等于队列头部的成本`q[0][1]`,这表明新计算的成本更优或等价,并且更靠前(即更早使用),因此原有的队列头部成本不再是最优选择,应该被移除。通过这种方式,我们确保队列头部始终保持最小花费,从而在后续的计算中可以直接使用。这个过程是必要的,以维持队列的状态正确反映从任何位置开始的最优花费。
🦆
在使用双端队列存储状态时,如何确保队列始终保持花费递增的顺序?
为了保持队列花费递增的顺序,算法在添加新状态之前,会首先检查新计算的成本f是否小于等于队列头部的成本。如果是,算法会持续移除队列头部,直到队列头部的成本高于新成本f。这样做确保了只有最低的成本被保留在队列头部,而任何高于新成本的旧成本都被移除,保持了队列的成本递增。此外,每次对队尾的检查和移除操作也帮助维护了队列中的状态是当前和未来计算中可能用到的最优状态。

相关问题