题解:徒步旅行中的补给问题

256 阅读4分钟

题解:徒步旅行中的补给问题

徒步旅行中的补给问题 - MarsCode

问题描述

小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,包括定义变量,循环,双端队列的使用

代码逻辑
  1. 初始化

    • res 用于记录总花费。
    • bag 是一个双端队列,用于存储当前携带的食物价格。
  2. 遍历每一天

    • 对于每一天 i,首先检查 bag 中的食物价格是否比当前补给站的食物价格高。如果是,则从 bag 中移除这些食物,因为它们不再是最优选择。
    • 然后,如果 bag 中的食物数量小于 k,则将当前补给站的食物价格添加到 bag 中。
    • 最后,将 bag 中最便宜的食物价格(即 bag[0])加到 res 中,并从 bag 中移除这个食物。
  3. 返回结果

    • 最终返回 res,即完成旅行的最小花费。

单调队列和贪心算法

单调队列

单调队列是一种特殊的双端队列,其中元素保持单调递增或单调递减的顺序。它通常用于解决以下类型的题目:

  • 滑动窗口最大值/最小值:在一个数组中,找到每个长度为 k 的滑动窗口的最大值或最小值。
  • 优化队列操作:在某些需要频繁插入和删除操作的场景中,保持队列的单调性可以优化算法的时间复杂度。
贪心算法

贪心算法是一种在每一步选择中都采取在当前状态下最优的选择,以期望最终得到全局最优解的算法。它通常用于解决以下类型的题目:

  • 最小生成树:如 Prim 算法和 Kruskal 算法。
  • 最短路径:如 Dijkstra 算法。
  • 区间调度问题:如活动选择问题,选择尽可能多的不重叠区间。
  • 背包问题:如分数背包问题,选择单位价值最高的物品。

总结

  • 单调队列适用于需要维护一个单调序列的场景,特别是在滑动窗口问题中。
  • 贪心算法适用于每一步选择最优解可以最终得到全局最优解的问题。

在你的代码中,你结合了单调队列和贪心算法的思想:

  • 使用单调队列 bag 来维护当前携带的食物价格,确保队列中的价格是单调递增的。
  • 使用贪心策略,在每一步选择当前最便宜的食物价格,以最小化总花费。

这种结合使得你的代码能够高效地解决题目中的问题。