购买水果需要的最少金币数 II
难度:
标签:
题目描述
代码结果
运行时间: 216 ms, 内存: 30.6 MB
/*
题目:购买水果需要的最少金币数 II
题目思路:
1. 初始化一个dp数组,其中dp[i]表示到达i重量时的最小金币数。
2. 遍历每个水果的重量和对应的金币数。
3. 对于每个水果,从总重量的末尾开始向前遍历,更新dp数组。
4. 最后,返回dp数组中表示总重量的最小金币数。
使用Java Stream:
*/
import java.util.stream.IntStream;
public int minCostToBuyFruits(int[] weight, int[] cost, int totalWeight) {
int[] dp = IntStream.generate(() -> Integer.MAX_VALUE).limit(totalWeight + 1).toArray();
dp[0] = 0;
IntStream.range(0, weight.length).forEach(i ->
IntStream.iterate(totalWeight, j -> j >= weight[i], j -> j - 1).forEach(j -> {
if (dp[j - weight[i]] != Integer.MAX_VALUE) {
dp[j] = Math.min(dp[j], dp[j - weight[i]] + cost[i]);
}
})
);
return dp[totalWeight] == Integer.MAX_VALUE ? -1 : dp[totalWeight];
}
解释
方法:
这个题解采用了动态规划加上双端队列的优化方法来解决问题。该方法的基本思想是使用一个动态规划数组dp,其中dp[i]表示购买到第i-1个水果时所需的最小金币数。初始状态是dp[1]等于第一个水果的价格。接下来,我们用一个双端队列st来存储那些可能是最优购买策略的水果的索引。为了维持队列的特性,我们会在移动到下一个水果时,从队列前端移除那些不再可能是最优策略的索引,并且从后端移除那些价格加上之前的最小花费比当前水果的价格加上当前最小花费更高的索引。然后将当前索引添加到队列中。队列的第一个元素总是给出了到当前水果为止的最小花费。队列的维护保证了每个元素最多只被加入和移除一次,从而提高了效率。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
动态规划数组dp[i]表示购买到第i-1个水果时所需的最少金币数。为何dp数组需要初始化为n+1长度,而非n长度?
▷🦆
在算法中,移除双端队列前端不再可能是最优策略的索引的条件为`2 * st[0] + 1 < i`。这个条件是如何确定的,它的逻辑依据是什么?
▷🦆
双端队列中,当`prices[st[-1]] + dp[st[-1]] >= prices[i] + dp[i]`时,会移除队列的后端索引。这种移除策略的目的和效果是什么?
▷