问题描述
小R正在计划一次从地点A到地点B的徒步旅行,总路程需要 N 天。为了在旅途中保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R在路途中每天都会经过一个补给站,可以购买食物进行补充。然而,每个补给站的食物每份的价格可能不同,并且小R最多只能同时携带 K 份食物。
现在,小R希望在保证每天都有食物的前提下,以最小的花费完成这次徒步旅行。你能帮助小R计算出最低的花费是多少吗?
测试样例
样例1:
输入:n = 5 ,k = 2 ,data = [1, 2, 3, 3, 2] 输出:9
样例2:
输入:n = 6 ,k = 3 ,data = [4, 1, 5, 2, 1, 3] 输出:9
样例3:
输入:n = 4 ,k = 1 ,data = [3, 2, 4, 1] 输出:10
题目分析
小R需要在N天内完成从地点A到地点B的徒步旅行。每一天,他需要消耗1份食物,而最多可以 携带K份食物。每天途经的补给站有不同的食物价格。目标是以最小的花费完成这段旅程。 关键点在于:
- 每天都需要一份食物。
- 购买策略需结合最大携带量K和未来价格信息来规划,以避免不必要的高花费。
- 寻找最低成本的问题需要动态调整购买和消耗决策。
解题思路
关键点1:动态规划 VS 贪心
-
动态规划可以解决此类问题,但复杂度较高,尤其是需要枚举每天的购买策略,会涉及多个状态的转移。
-
贪心算法更适合本题,因为:
- 小R每天都必须有食物,因此我们只需要确保当前和未来的食物以最优价格购买即可
- 小R每天最多携带K份食物,因此我们只需要关注最多K天内的价格。
关键点2:维护价格的“最优队列”
为满足贪心策略,我们需要维护一个动态的最优选择队列:
- 队列中存储的是当前可选价格的下标。
- 队列保持单调递增,即队首总是当前窗口内的最小价格。
贪心策略
- 购买策略:
- 每天从当前可用范围(至多K天内)的价格中选择最便宜的一份购买。
- 如果当天食物足够(前K天购买的食物还未消耗完),则不需要额外购买。
- 移除过期价格:
- 超出K天范围的价格无效,需要从队列中移除。
- 更新窗口:
- 每经过一天,需将当天的价格加入队列并重新维护单调性。
解题步骤
第1步:初始化
- 维护一个双端队列 ready,保存当前可选的最优价格(队列按递增排序)。
- 初始化总花费变量 min_money=0。
第2步:遍历每一天
对于每天i:
- 将当天价格加入队列:
- 如果队列末尾的价格比当天价格高,则移除末尾元素(保证队列递增)。
- 将当天价格下标加入队列。
- 移除过期的价格:
- 如果队首的下标不在当前携带范围内(小于 i - k + 1),移除队首。
- 消耗食物:
- 从队列队首获取最便宜的价格进行购买,并累加到总花费min_money。
第3步:返回结果
最终输出累加的总花费。
代码实现
from collections import deque
def solution(n, k, data):
"""
n: 总天数
k: 最大携带食物数
data: 每天补给站食物的价格数组
"""
# 初始化总花费
min_money = 0
# 双端队列,存储当前可用价格的下标(队列保持递增)
ready = deque()
for i in range(n):
# Step 1: 将当前价格加入队列,保持递增
while ready and data[ready[-1]] > data[i]:
ready.pop()
ready.append(i)
# Step 2: 移除过期的价格(超出携带范围)
if ready[0] < i - k + 1:
ready.popleft()
# Step 3: 消耗食物,从队首选择最优价格购买
min_money += data[ready[0]]
return min_money
总结
- 本题采用贪心+双端队列实现,每一步均保持最优选择。
- 时间复杂度 O(N),空间复杂度 O(K),适合处理大规模数据。