Marcode徒步旅行中的补给问题(python)

问题描述

小R正在计划一次从地点A到地点B的徒步旅行,总路程需要 N天。为了在旅途中保持充足的能量,小R每天必须消耗1份食物。幸运的是,小R在路途中每天都会经过一个补给站,可以先购买完食物后再消耗今天的1份食物。然而,每个补给站的食物每份的价格可能不同,并且小R在购买完食物后最多只能同时携带 K 份食物。
现在,小R希望在保证每天食物消耗的前提下,以最小的花费完成这次徒步旅行。你能帮助小R计算出最低的花费是多少吗?
输入
• n 总路程需要的天数
• k 小R最多能同时携带食物的份数
• data[i] 第i天补给站每份食物的价格
输出
• 返回完成这次徒步旅行的最小花费
约束条件
• 1 < n,k < 1000
• 1 < data[i] < 10000
测试样例
样例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

思路

刚开始本来想用动态规划来解决,但是写出来只能满足实例给的几个结果后面在网上找到的都是c++和Java解决的,最后我采用的是滑动窗口结合双端队列来解决的。
我先给出错误的那个代码

def solution(n, k, data):
    current_food = 0
    total_cost = 0

    for i in range(n):
        # 如果当前食物不足,需要购买
        if current_food == 0 or data[i] == min(data[i:i+k]):
            # 计算需要购买的食物量
            buy_amount = min( k - current_food,n-i)  # 最多购买 k 份,或者剩余天数
            # 检查当前价格是否是接下来 k 天中最便宜的
            if data[i] == min(data[i:i+buy_amount]):
                # 如果是,购买足够的食物
                if i == n - 1 and current_food > 0:#判断最后一天是否需要购买
                    break
                total_cost += buy_amount * data[i]
                current_food = buy_amount +current_food
            else:
                # 如果不是,只购买一天的食物
                total_cost += data[i]
                current_food = 1
        # 每天消耗一份食物
        current_food -= 1

    return total_cost

if __name__ == "__main__":
    print(solution(5, 2, [1, 2, 3, 3, 2]))#输出9
    print(solution(6, 3, [4, 1, 5, 2, 1, 3]))#输出9
    print(solution(13, 6, [6,19,19,3,3,25,16,17,8,1,5,21,2]))#应该输出40才对

调试了之后发现主要问题是,无法计算如何购买足够撑到最便宜那天的食物数量,不知道也没有大神可以给给建议

滑动窗口+双端队列(正确代码)

from collections import deque


def solution(n: int, k: int, data: list) -> int:
"""这两个断言用于确保输入的 n 和 k 满足题目要求。"""
    assert n == len(data)
    assert k < n

    mins = deque()#双端队列 deque 来维护一个单调递增的队列,用于存储价格信息。
    result = 0

    for j in range(n):
    # 如果当前队列不为空,且队列中的最后一个元素的值大于当前元素,则移除队列中的元素
        while len(mins) > 0 and mins[-1][1] > data[j]:
            mins.pop()
        mins.append([j, data[j]])# 将当前元素的索引和数据添加到队列
        while mins[0][0] <= j - k:# 如果队列头部的元素不在滑动窗口范围内,移除队列头部的元素
            mins.popleft()
        # 累加当前窗口的最小值(队列头部的值)
        result += mins[0][1]

    return result


if __name__ == "__main__":
    # Add your test cases here

    print(solution(5, 2, [1, 2, 3, 3, 2]) == 9)

这个算法的时间复杂度是 O(n),因为每个元素最多被加入和移除队列一次
对于每个索引 j,我们维护一个窗口,该窗口的大小最大为 k,并确保队列中存储的是当前窗口的最小值。
我们通过双端队列(deque)来维护窗口中的最小值。双端队列的优势在于,它允许我们以常数时间复杂度 O(1) 从队列的两端删除元素,从而加快了计算过程。
示例解释:
假设我们调用 solution(5, 2, [1, 2, 3, 3, 2]):

初始时,mins 是空的,result 为 0。

对于每个元素,维护队列,使得队列始终存储当前窗口的最小值。

j = 0:当前元素为 1,队列为空,加入队列 [0, 1]。窗口最小值为 1,result 变为 1。
j = 1:当前元素为 2,队列中的元素 1 小于 2,加入队列 [0, 1], [1, 2]。窗口最小值为 1,result 变为 2。
j = 2:当前元素为 3,队列中的元素 1 小于 3,加入队列 [0, 1], [1, 2], [2, 3]。窗口最小值为 1,result 变为 3。
j = 3:当前元素为 3,队列中的元素 1 小于 3,加入队列 [0, 1], [1, 2], [2, 3], [3, 3]。窗口最小值为 2,result 变为 5。
j = 4:当前元素为 2,队列中的元素 2 和 3 被移除,加入队列 [1, 2], [4, 2]。窗口最小值为 2,result 变为 7。
最终返回的结果为 9。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值