2 徒步旅行中的补给问题 题解 | 豆包MarsCode AI刷题

90 阅读4分钟

问题描述

小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份食物。每天途经的补给站有不同的食物价格。目标是以最小的花费完成这段旅程。 关键点在于:

  1. 每天都需要一份食物。
  2. 购买策略需结合最大携带量K和未来价格信息来规划,以避免不必要的高花费。
  3. 寻找最低成本的问题需要动态调整购买和消耗决策。

解题思路

关键点1:动态规划 VS 贪心

  1. 动态规划可以解决此类问题,但复杂度较高,尤其是需要枚举每天的购买策略,会涉及多个状态的转移。

  2. 贪心算法更适合本题,因为:

  • 小R每天都必须有食物,因此我们只需要确保当前和未来的食物以最优价格购买即可
  • 小R每天最多携带K份食物,因此我们只需要关注最多K天内的价格。

关键点2:维护价格的“最优队列”

为满足贪心策略,我们需要维护一个动态的最优选择队列:

  • 队列中存储的是当前可选价格的下标。
  • 队列保持单调递增,即队首总是当前窗口内的最小价格。

贪心策略

  1. 购买策略
  • 每天从当前可用范围(至多K天内)的价格中选择最便宜的一份购买。
  • 如果当天食物足够(前K天购买的食物还未消耗完),则不需要额外购买。
  1. 移除过期价格
  • 超出K天范围的价格无效,需要从队列中移除。
  1. 更新窗口:
  • 每经过一天,需将当天的价格加入队列并重新维护单调性。

解题步骤

第1步:初始化

  • 维护一个双端队列 ready,保存当前可选的最优价格(队列按递增排序)。
  • 初始化总花费变量 min_money=0。

第2步:遍历每一天

对于每天i:

  1. 将当天价格加入队列:
  • 如果队列末尾的价格比当天价格高,则移除末尾元素(保证队列递增)。
  • 将当天价格下标加入队列。
  1. 移除过期的价格
  • 如果队首的下标不在当前携带范围内(小于 i - k + 1),移除队首。
  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),适合处理大规模数据。