第六届字节跳动青训营第一课 | 豆包MarsCode AI 刷题

94 阅读3分钟

补给站最优花费问题

问题重述:

AA地到BB地需要MM天,每过一天需要一份食物,食物可以从补给站购买,路上总共有NN个补给站,PP为每个补给站的描述,共两个数字XXYY,第一个表示第XX天有一个补给站,并且该站每份食物价格为YY

实际上是一个非常经典的加油站最优解问题。

思路:

AABB共需要MM天,即MM份食物,由于每个站点可购买的食物不设置上限,那么我们只需要在每过一天在曾经经过的花费最少的站点购买食物,也即贪心算法

如何取得获取花费最少的站点,我们可以通过一个最大堆来维护所经过的所有站点,由于此题站点食物不设上限,则可以优化为只需要记住最便宜的花费即可。

代码实现:

定义min_cost记录所经过的站点的最小值

定义sum记录最终结果

定义j遍历p列表元素

通过循环遍历每一天,每经过一天则需要购买一份食物

在每次遍历之前,判断当前天数是否有站点

若当前天数有站点则将其花费与min_cost进行比较更新

而后在最终结果sum上增加min_cost

代码如下

def solution(n, k, p):
    # Edit your code here
    min_cost = 1e9
    sum = 0
    j = 0
    for i in range(0,n):
        if i == p[j][0]:
            min_cost = min(min_cost,p[j][1])
            if j < k-1:
                j+=1
        sum+=min_cost
    return sum


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

    print(solution(5, 4, [[0, 2], [1, 3], [2, 1], [3, 2]]) == 7)

时间效率:O(n)O(n)

空间效率:O(1)O(1)

细节

  • 为什么使用min_cost而不是min_cost[]?

    因为每个站点所能购买的食物不设上限,我们只需要一直在所经过的最优站点一直购买即可,所以无需使用最大堆存储其他站点的数据,只需要进行更新

  • if j < k-1:的作用:

    防止j越界

    题目进阶:

    如果每个站点的食物是有限mm

    题目做法则为经典的最大堆贪心

    我们需要引入一个最大堆来维护所有站点

    把每个经过的站点放入优先队列中,优先进行最小数值的花费,当最小花费的站点没有食物之后弹出队列

    继续从队列的头部更新,优先队列会自行维护从小到大的顺序

    优先队列:

    对于python中的优先队列可以手动维护也可以引入库函数。

    优先队列本质上是通过堆排序进行维护整个数据结构。

    手动堆排序维护在此不再赘述。

    讲解一下对于库函数的调用。

    Python中需要引入queue中的PriorityQueue

    使用方法如下

    from queue import PriorityQueue
    
    q = PriorityQueue()
    
    q.put((2, 'code'))
    q.put((1, 'eat'))
    q.put((3, 'sleep'))
    
    while not q.empty():
        next_item = q.get()
        print(next_item)
    
    # 结果:
    #   (1, 'eat')
    #   (2, 'code')
    #   (3, 'sleep')
    
    

    对于c++则直接引入queue库即可

    在此给出例题

    lc的871.最低加油次数

    leetcode.cn/problems/mi…

    并在此附上本人的该题做法仅供参考

     class Solution {
     public:
         int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
             priority_queue<int> pq;
             int nowFuel = startFuel;
             int stops = 0, i = 0;
             // 循环直到当前油量能够到达目的地
             while (nowFuel < target) {
                 // 将所有可以到达的加油站的燃料添加到最大堆中
                 while (i < stations.size() && stations[i][0] <= nowFuel) {
                     pq.push(stations[i][1]);
                     i++;
                 }
                 // 如果没有加油站可以利用,并且无法继续前进,返回 -1
                 if (pq.empty()) return -1;
    
                 // 从最大堆中取出最多燃料的加油站
                 nowFuel += pq.top();
                 pq.pop();
                 stops++; // 加油一次
             }
             return stops;
         }
     };