68.绿洲之旅:最少补给次数探索|Marscode AI刷题

2 阅读3分钟

1.题目

问题描述

小U正在准备穿越一片广阔的沙漠。沙漠里有多个绿洲,每个绿洲设有补给站。所有补给站的收费相同,但提供的水量不同。从起点到终点共有 (D) 公里,小U需要规划在哪些补给站停留,以保证整个旅途中水的供应。

起点到终点的距离为 (D) 公里,小U初始携带 (W) 升水,每行走 1 公里消耗 1 升水。小U希望通过最少的补给次数安全到达终点。每个补给站的位置由 position[i] 表示,距离起点的公里数,supply[i] 表示该站可以提供的水量,单位为升。

请你帮助小U计算,要安全到达终点至少需要补给几次,或者判断是否根本无法到达终点,此时输出-1。


测试样例

样例1:

输入:d = 10, w = 4, position = [1, 4, 7], supply = [6, 3, 5]

输出:1

样例2:

输入:d = 15, w = 3, position = [3, 6, 12], supply = [4, 5, 2]

输出:-1

样例3:

输入:d = 20, w = 10, position = [5, 15], supply = [8, 6]

输出:2

样例4:

输入:d = 7, w = 7, position = [], supply = []

输出:0

样例5:

输入:d = 25, w = 8, position = [7, 13, 18], supply = [7, 6, 5]

输出:3

2.思路

计算至少需要补给几次,使用贪心算法,优先选择补给多的点。

思路一:

如果当前水量走不到终点→当前是补给最多的点或当前水量不够走到下一个补给站→补给

思路二:√

建立一个最大堆,每经过一个补给站,就把这个补给站加入最大堆,一直到水不够的时候,就从堆中取最大的补给站补给

  • 思路一是直接的贪心策略,虽然简单,但可能会在一些情况下不是最优解。例如,当你遇到一个补给站的水量相对较少,但它恰好是最优的补给站时,贪心的选择可能会导致最终的解决方案不是最优的。
  • 思路二使用了最大堆,可以保证每次补给都是从水量最多的站点中取出,因此能更好地利用水资源,可能会得到更好的解答。而且,最大堆保证了每次选择的补给站是当前最优的,避免了贪心的局限性。

3.代码

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

int solution(int d, int w, std::vector<int> position, std::vector<int> supply) {
    // Please write your code here
    int n = position.size();
    // 将起点添加到补给站的列表中
    position.push_back(0);
    supply.push_back(w);

    // 补给站信息加入stations
    vector<pair<int, int>> stations;
    for (int i = 0; i < n; i++) {
        stations.push_back({position[i], supply[i]});
    }
    stations.push_back({d, 0}); // 终点

    sort(stations.begin(), stations.end()); // 按照位置升序排列

    priority_queue<int> maxHeap; // 用于存储经过的补给站的水量(最大堆)
    int currentWater = w;
    int count = 0;
    int prePosition = 0;

    for (const auto& station : stations) {
        int distance = station.first - prePosition;
        currentWater -= distance;

        // 当水不足以到达下一个站点时,从堆中补给
        while (currentWater < 0 && !maxHeap.empty()) {
            currentWater += maxHeap.top();
            maxHeap.pop();
            count++;
        }
        // 如果仍然水不足,说明无法到达终点
        if (currentWater < 0) {
            return -1;
            }
        // 将当前补给站的水量加入堆
        maxHeap.push(station.second);
        prePosition = station.first;
    }
    return count;
}

int main() {
    //  You can add more test cases here
    std::vector<int> testPosition = {170, 192, 196, 234, 261, 269, 291, 404, 1055, 1121, 1150, 1234, 1268, 1402, 1725, 1726, 1727, 1762, 1901, 1970};
    std::vector<int> testSupply = {443, 185, 363, 392, 409, 358, 297, 70, 189, 106, 380, 130, 126, 411, 63, 186, 36, 347, 339, 50};

    std::cout << (solution(10, 4, std::vector<int>{1, 4, 7}, std::vector<int>{6, 3, 5}) == 1) << std::endl;
    std::cout << (solution(2000, 200, testPosition, testSupply) == 5) << std::endl;

    return 0;
}

注意:

  • std::sort 默认会对 pair 进行排序时,首先按照 第一个元素 排序。如果第一个元素相同,才会按照 第二个元素 排序。
  • priority_queue 默认是一个 最大堆(最大优先队列),即队头的元素总是当前队列中的最大元素。