每日一题:补给站最优花费问题 | 豆包MarsCode AI刷题

126 阅读5分钟

每日一题:补给站最优花费问题 | 豆包MarsCode AI刷题

问题描述

小U计划进行一场从地点A到地点B的徒步旅行,旅行总共需要 M 天。为了在旅途中确保安全,小U每天都需要消耗一份食物。在路程中,小U会经过一些补给站,这些补给站分布在不同的天数上,且每个补给站的食物价格各不相同。

小U需要在这些补给站中购买食物,以确保每天都有足够的食物。现在她想知道,如何规划在不同补给站的购买策略,以使她能够花费最少的钱顺利完成这次旅行。

  • M:总路程所需的天数。
  • N:路上补给站的数量。
  • p:每个补给站的描述,包含两个数字 AB,表示第 A 天有一个补给站,并且该站每份食物的价格为 B 元。

保证第0天一定有一个补给站,并且补给站是按顺序出现的。


测试样例

样例1:

输入:m = 5 ,n = 4 ,p = [[0, 2], [1, 3], [2, 1], [3, 2]] 输出:7

样例2:

输入:m = 6 ,n = 5 ,p = [[0, 1], [1, 5], [2, 2], [3, 4], [5, 1]] 输出:6

样例3:

输入:m = 4 ,n = 3 ,p = [[0, 3], [2, 2], [3, 1]] 输出:9

上述代码实现了一个动态规划的思路,用来计算在一段时间内的最小食物成本。它根据每天的食物价格动态调整,从多个补给站中选择最优方案。

实现思路

输入参数
  1. m:天数(总共持续多少天)。

  2. n:补给站的数量。

  3. p:一个二维数组,描述每个补给站的信息。每个补给站用一个 [day, price] 表示,其中:

    • day 表示第几天有补给站。
    • price 表示该补给站当天的食物价格。
输出结果

返回整个时间段内的最小食物成本。

主要逻辑
  1. 初始化最小成本和当前最小成本

    • min_cost 初始化为 0,表示累计的最小食物成本。
    • min_now 初始化为第 0 天的补给站价格,表示当前已知的最小价格。
  2. 遍历每一天

    • 判断当天是否有补给站:

      • 有补给站:找到当天的补给价格,将当前最小价格更新为当天价格与之前的最小价格中的较小值。
      • 没有补给站:当天的价格直接继承之前的最小价格。
    • 将当前最小价格累加到 min_cost 中,表示这一天天食物的费用。

  3. 返回结果: 最终累计的 min_cost 是整个时间段的最小食物成本。


代码解读

初始状态
min_cost = 0
min_now = p[0][1]  # 初始化为第 0 天补给站的价格
  • 将总成本初始化为 0。
  • 用第 0 天的补给站价格作为当前已知的最小价格。
遍历每一天
for day in range(m):
  • 从第 0 天开始,逐天遍历,直到第 m-1 天。
检查是否有补给站
if day in [station[0] for station in p]:
  • 检查当前天数是否存在补给站。
  • 使用列表解析从 p 中提取所有有补给站的天数,判断当前天数是否在其中。
有补给站时
for station in p:
    if station[0] == day:
        result = station[1]
min_now = min(min_now, result)
min_cost += min_now
  • 找到当天补给站的价格 result
  • 更新当前的最小价格 min_now 为当天价格和之前最小价格中的较小值。
  • 累加当天的最小价格到总成本。
没有补给站时
else:
    min_cost += min_now
  • 直接使用之前已知的最小价格作为当天价格,并将其累加到总成本。

示例解读

示例输入
m = 5
n = 4
p = [[0, 2], [1, 3], [2, 1], [3, 2]]
  • 总共 5 天(m=5)。
  • 有 4 个补给站(n=4),分别在第 0、1、2、3 天,价格为 2、3、1、2。
过程分析
  1. 第 0 天:补给站价格为 2,min_now = 2min_cost = 2
  2. 第 1 天:补给站价格为 3,min_now = min(2, 3) = 2min_cost = 2 + 2 = 4
  3. 第 2 天:补给站价格为 1,min_now = min(2, 1) = 1min_cost = 4 + 1 = 5
  4. 第 3 天:补给站价格为 2,min_now = min(1, 2) = 1min_cost = 5 + 1 = 6
  5. 第 4 天:无补给站,价格沿用前一天最小价格 min_now = 1min_cost = 6 + 1 = 7
输出结果
7

完整代码

def solution(m, n, p):
    # dp数组的含义:第i天结束时剩余j份补给所花费的价钱
    dp = [[float('inf')] * (m + 1) for _ in range(m + 1)]
    
    # 标记下一个补给站的索引
    index = 0
    
    # 初始化dp数组
    for i in range(m + 1):
        dp[0][i] = p[index][1] * i
    
    index += 1
    
    # 第i天
    for i in range(1, m + 1):
        # 剩余j份食物
        for j in range(m):
            # 剩余的食物足够走完了
            if j >= i - m:
                dp[i][j] = dp[i - 1][j + 1]
            
            # 不补给的情况
            dp[i][j] = dp[i - 1][j + 1]
            
            # 判断今天是否到达补给站
            if index < len(p) and p[index][0] == i:
                # 有补给站,购买k份食物
                for k in range(j + 1):
                    dp[i][j] = min(dp[i][j], dp[i - 1][j - k + 1] + k * p[index][1])
        
        # 如果今天有补给站,补给站指针移动
        if index < len(p) and p[index][0] == i:
            index += 1
    
    return dp[m][0]
​
if __name__ == "__main__":
    # Add your test cases here
    print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]]) == 7)