学习笔记:补给站最优花费问题解析 | 豆包MarsCode AI刷题
题目解析
问题描述
小U计划进行一场从地点A到地点B的徒步旅行,旅行总共需要 M 天。为了在旅途中确保安全,小U每天都需要消耗一份食物。在路程中,小U会经过一些补给站,这些补给站分布在不同的天数上,且每个补给站的食物价格各不相同。
小U需要在这些补给站中购买食物,以确保每天都有足够的食物。现在她想知道,如何规划在不同补给站的购买策略,以使她能够花费最少的钱顺利完成这次旅行。
M:总路程所需的天数。N:路上补给站的数量。p:每个补给站的描述,包含两个数字A和B,表示第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
题目分析
我们需要帮助小U规划她的徒步旅行补给计划,目标是以最低成本购买所需的食物。题目给出的条件包括:
- 旅行天数 M:小U需要进行 M 天的徒步旅行。
- 补给站数量 N:途中有 N 个补给站。
- 补给站信息 p:每个补给站包含两部分数据,第 A 天的补给站,和每份食物的价格 B。
小U每天都需要消耗一份食物,我们需要确保她每天都有足够的食物供给,同时总花费最少。
思路解析
本问题需要规划一种策略,以最低花费购买 M 天所需的食物。为了解决这个问题,我们可以使用贪心策略为核心的算法,以下是解题的核心思路:
-
每日需求:
- 小U每天都需要一份食物,因此问题可以简化为:每天选择一个有效补给站,购买最便宜的食物。
-
贪心选择策略:
- 贪心算法的核心在于局部最优解:每天都从当前可用的补给站中选择价格最低的补给站购买。
- 由于补给站只能在当天及之前开放,因此每天的选择范围受到限制。
-
实现步骤:
- 按天循环,从第 0 天开始,到第M-1 天结束。
- 对每一天,查找当天及之前开放的所有补给站,选择价格最低的一个补给站购买食物。
- 累加花费并更新到下一天,直至完成所有 M 天的补给。
图解示意
以下通过图表详细说明样例 1 的求解过程:
输入数据
- M=5, N=4, p=[[0,2],[1,3],[2,1],[3,2]]。
图解过程
| 天数 | 当前可用补给站 | 选择最优补给站(价格) | 当天花费 | 累计总花费 |
|---|---|---|---|---|
| 0 | [0 (2)] | 0 天补给站(2 元) | 2 元 | 2 元 |
| 1 | [0 (2), 1 (3)] | 0 天补给站(2 元) | 2 元 | 4 元 |
| 2 | [0 (2), 1 (3), 2 (1)] | 2 天补给站(1 元) | 1 元 | 5 元 |
| 3 | [0 (2), 1 (3), 2 (1), 3 (2)] | 2 天补给站(1 元) | 1 元 | 6 元 |
| 4 | [0 (2), 1 (3), 2 (1), 3 (2)] | 2 天补给站(1 元) | 1 元 | 7 元 |
最终输出:7。
图解表明,贪心算法通过逐天选择最低价格的补给站,能够以最小成本完成旅行。
代码详解
以下代码基于贪心算法逐天选择最优价格的补给站:
def solution(M, N, p):
total_cost = 0 # 总花费
current_day = 0 # 当前天数
while current_day < M:
# 找到当前天数及之前的最便宜的补给站
min_price = float('inf') # 初始设为正无穷大
for station in p:
if station[0] <= current_day: # 仅考虑当天及之前的补给站
min_price = min(min_price, station[1])
# 购买食物并更新总花费
total_cost += min_price
current_day += 1
return total_cost
if __name__ == "__main__":
# 测试样例
print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]])) # 输出: 7
print(solution(6, 5, [[0, 1], [1, 5], [2, 2], [3, 4], [5, 1]])) # 输出: 6
print(solution(4, 3, [[0, 3], [2, 2], [3, 1]])) # 输出: 9
代码运行逻辑
-
初始化:
total_cost:记录总花费,初始值为 0。current_day:记录当前天数,从 0 开始。
-
查找最低价格:
- 遍历补给站列表 p,筛选出当前天数 currentdaycurrent_day 及之前的补给站。
- 使用
min()函数实时更新最低价格。
-
更新状态:
- 累加当天购买食物的花费到
total_cost。 - 递增
current_day进入下一天。
- 累加当天购买食物的花费到
-
循环结束条件:
- 循环 current_day 至 M-1,完成所有 M 天的补给。
样例测试
if __name__ == "__main__":
print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]])) # 输出: 7
print(solution(6, 5, [[0, 1], [1, 5], [2, 2], [3, 4], [5, 1]])) # 输出: 6
print(solution(4, 3, [[0, 3], [2, 2], [3, 1]])) # 输出: 9
性能分析
-
时间复杂度:
- 外层循环执行 M 次,内层循环遍历 N 个补给站,总复杂度为 O(M⋅N)。
-
空间复杂度:
- 仅使用常量空间存储总花费和当前天数,复杂度为 O(1)。
优化与扩展
-
优化数据结构:
- 可使用优先队列(最小堆)维护当前可用补给站的价格,使得查询最小价格的复杂度降低为 O(logN)。
-
扩展问题:
- 考虑补给站食物数量有限的情况,增加动态规划以处理更复杂的需求。
知识总结
新知识点
-
贪心算法的应用:
- 贪心策略的核心是局部最优解。
- 本题中,每天都选择价格最低的补给站来达到最低总花费。
-
浮点数的无穷大表示:
float('inf')用于初始化最小值或最大值,在比较过程中逐渐更新。
-
输入数据处理:
- 理解题目时要准确提取输入格式(如补给站的时间和价格)。
学习总结
通过本题的解析,我们学习了如何应用贪心算法解决实际问题。局部最优策略在类似优化问题中非常高效。理解问题的本质,结合贪心策略和清晰的逻辑实现,是完成这类问题的关键。通过对题目的解析与总结,我们不仅能够提升解题能力,还能系统性地巩固算法知识。
学习心得
-
从样例入手理解题目:可以从通过样例数据进行分析,手动计算一次,帮助分析潜在的规律和逻辑。
-
刚开始刷题时,可能会遇到没有思路无从下手,或者有思路没有办法转化为代码的问题,这时候可以通过豆包MarsCode AI进行对话,从中获取一部分的思路或代码,读懂理解后再进行刷题,这一定程度上提高了我的学习效率。
-
最后一定要动手实践代码:理解代码逻辑后,一定要亲自实现,不能眼高手低,重新编程实现,这样可以检验自己是否真正掌握了这个算法的核心要点,同时也可以发现遗漏的细节或优化点。