徒步旅行中的补给问题
小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
这是一个典型的动态规划问题,其中目标是计算在有限的食物容量 K 和每日食物价格不同的情况下,如何以最小的花费完成徒步旅行。可以通过动态规划方法来实现这一点。
问题分析:
- 小R每天都会消耗1份食物,且每天经过一个补给站。
- 补给站的食物价格不同,但每个补给站最多只能携带
K份食物。 - 小R的目标是用最小的花费从起点到达终点。
动态规划状态定义:
我们可以定义一个状态 dp[i],表示从第0天到第i天的最低花费。为了解决问题,我们需要计算如何选择在哪些天补充食物,且要保证当天有食物,并且在价格较低时尽量补充足够的食物。
状态转移:
-
dp[0] = data[0],即第一天的费用为补给站1的费用。 -
对于第
i天的情况,我们要么直接从前一天携带食物过来,要么在之前的某一天从补给站购买更多食物。具体来说,
dp[i]可以通过遍历之前的j天来计算,假设我们从第j天携带了足够的食物到第i天,这样我们需要的费用为:dp[j] + (i - j) * data[j](即从第j天补给,携带食物直至第i天)。 -
为了减少不必要的计算,我们可以使用一个最小堆来维护在给定范围内的最优解。
代码实现:
pythonCopy Code
import heapq
def min_cost(n, k, data):
# dp[i] 表示到达第i天的最低花费
dp = [float('inf')] * n
dp[0] = data[0] # 第一天天必须购买1份食物
# 使用一个优先队列来存储能带过的食物补给站的最低花费
heap = [(data[0], 0)] # (价格, 天数)
for i in range(1, n):
# 考虑从第i天之前的每一天出发,并计算成本
# 需要找到从第i天到之前最多k天范围内最便宜的补给站
while heap and heap[0][1] < i - k: # 弹出过期的补给站
heapq.heappop(heap)
# dp[i] 选择从 heap 中最便宜的补给站
dp[i] = dp[heap[0][1]] + data[i] # 选择最便宜的补给站购买
# 维护当前的补给站
heapq.heappush(heap, (data[i], i)) # 添加当前补给站到队列
return dp[n - 1]
# 测试样例
print(min_cost(5, 2, [1, 2, 3, 3, 2])) # 输出 9
print(min_cost(6, 3, [4, 1, 5, 2, 1, 3])) # 输出 9
print(min_cost(4, 1, [3, 2, 4, 1])) # 输出 10
解释:
- 状态转移:通过使用
dp[i]来表示到达第i天的最低花费,使用一个最小堆来帮助我们选择最便宜的补给站,以确保最小花费。 - 动态规划数组
dp[i]:表示到达第i天时的最低花费,初始化为无穷大,dp[0]初始化为data[0],因为第一天必须购买食物。 - 优先队列(堆) :使用堆来维护最近的
k天中的最便宜的补给站,避免了逐个比较带来的重复计算。
时间复杂度:
- 时间复杂度:
O(n log k)。遍历每一天,每天对于每个补给站都要进行堆操作(插入和删除操作的时间复杂度是O(log k)),因此总时间复杂度为O(n log k)。 - 空间复杂度:
O(k),用于存储堆中最多k个元素。
测试:
-
样例 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
- 输入:
通过这个动态规划算法,我们能够以最小的花费解决这个问题。