题解:徒步旅行中的补给问题
问题描述
小R正在计划一次从地点A到地点B的徒步旅行,总路程需要 N 天。为了在旅途中保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R在路途中每天都会经过一个补给站,可以购买食物进行补充。然而,每个补给站的食物每份的价格可能不同,并且小R最多只能同时携带 K 份食物。
现在,小R希望在保证每天都有食物的前提下,以最小的花费完成这次徒步旅行。你能帮助小R计算出最低的花费是多少吗?
思考过程
我在做这题的过程中主要运用了贪心和单调队列的思想,如果每次遇到商店小R都将背包填满,就能保证他每天都有食物可以吃,但是我们还需要满足最小花费的条件,所以在进商店时食物价格比背包中的低,可以将贵的食物"卖出去",再低价买入当前商店的食物,这样就可以满足最小花费的条件
我用一个双端队列deque来表示背包,队列中存放食物的价格,队列的长度上限为背包容量 一次循环代表新的一天,这一天会遇到商店,然后消耗食物,我们只要保证队列不为空,并且队首的食物价格最低,并且每天只吃最便宜的食物,就能满足题目的要求
实现代码
from collections import deque
def solution(n, k, data):
# Edit your code here
res = 0
bag = deque()
for i in range(n):
# 遇到便宜的食物了,卖掉包里比它贵的,保证单调
while len(bag) > 0 and data[i] < bag[-1]:
bag.pop()
# 包里没有更贵的食物了,填满它
while len(bag) < k:
bag.append(data[i])
# 吃饭,结账
res += bag[0]
bag.popleft()
return res
if __name__ == "__main__":
# Add your test cases here
print(solution(5, 2, [1, 2, 3, 3, 2]) == 9)
print(solution(6, 3, [4, 1, 5, 2, 1, 3]) == 9)
学习心得和AI分析与总结
此前没有学习过python的语法和基本库的使用,再MarsCode的帮助下,我很快上手了python,包括定义变量,循环,双端队列的使用
代码逻辑
-
初始化:
res用于记录总花费。bag是一个双端队列,用于存储当前携带的食物价格。
-
遍历每一天:
- 对于每一天
i,首先检查bag中的食物价格是否比当前补给站的食物价格高。如果是,则从bag中移除这些食物,因为它们不再是最优选择。 - 然后,如果
bag中的食物数量小于k,则将当前补给站的食物价格添加到bag中。 - 最后,将
bag中最便宜的食物价格(即bag[0])加到res中,并从bag中移除这个食物。
- 对于每一天
-
返回结果:
- 最终返回
res,即完成旅行的最小花费。
- 最终返回
单调队列和贪心算法
单调队列
单调队列是一种特殊的双端队列,其中元素保持单调递增或单调递减的顺序。它通常用于解决以下类型的题目:
- 滑动窗口最大值/最小值:在一个数组中,找到每个长度为
k的滑动窗口的最大值或最小值。 - 优化队列操作:在某些需要频繁插入和删除操作的场景中,保持队列的单调性可以优化算法的时间复杂度。
贪心算法
贪心算法是一种在每一步选择中都采取在当前状态下最优的选择,以期望最终得到全局最优解的算法。它通常用于解决以下类型的题目:
- 最小生成树:如 Prim 算法和 Kruskal 算法。
- 最短路径:如 Dijkstra 算法。
- 区间调度问题:如活动选择问题,选择尽可能多的不重叠区间。
- 背包问题:如分数背包问题,选择单位价值最高的物品。
总结
- 单调队列适用于需要维护一个单调序列的场景,特别是在滑动窗口问题中。
- 贪心算法适用于每一步选择最优解可以最终得到全局最优解的问题。
在你的代码中,你结合了单调队列和贪心算法的思想:
- 使用单调队列
bag来维护当前携带的食物价格,确保队列中的价格是单调递增的。 - 使用贪心策略,在每一步选择当前最便宜的食物价格,以最小化总花费。
这种结合使得你的代码能够高效地解决题目中的问题。