更多算法题的题解见:算法刷题题解汇总(持续更新中)
一、问题背景
小U正在准备穿越一片广阔的沙漠。沙漠里有多个绿洲,每个绿洲设有补给站。所有补给站的收费相同,但提供的水量不同。从起点到终点共有 (D) 公里,小U需要规划在哪些补给站停留,以保证整个旅途中水的供应。
起点到终点的距离为 (D) 公里,小U初始携带 (W) 升水,每行走 1 公里消耗 1 升水。小U希望通过最少的补给次数安全到达终点。每个补给站的位置由 position[i]
表示,距离起点的公里数,supply[i]
表示该站可以提供的水量,单位为升。
计算要安全到达终点至少需要补给几次,或者判断是否根本无法到达终点,此时输出-1。
二、问题分析
- 小U从起点出发,终点距离为D公里,初始携带W升水,每行走1公里消耗1升水。
- 沙漠中有多个补给站,每个补给站的位置和提供的水量各不相同。
- 小U希望通过最少的补给次数安全到达终点。
这是一个典型的贪心算法问题,我们需要在每一步选择最优的补给策略,以减少总的补给次数。
三、解题思路
- 初始化最大堆:为了能够快速获取当前情况下最优的补给选择,我们使用一个最大堆来存储可用的补给站水量。
- 遍历补给站:从起点开始,依次遍历每个补给站,计算到达下一个补给站所需消耗的水量。
- 检查水量:如果当前水量不足以到达下一个补给站,则从最大堆中取出最大水量进行补给,并增加补给次数。
- 更新位置:到达补给站后,更新当前位置,并将该补给站的水量加入最大堆。
- 到达终点检查:遍历完所有补给站后,检查从最后一个补给站到终点的距离,并计算所需的水量。如果当前水量不足以到达终点,则继续从最大堆中取出最大水量进行补给。
- 输出结果:如果能够到达终点,则返回补给次数;否则,返回-1。
参考代码如下:
public class Main {
public static int solution(int d, int w, int[] position, int[] supply) {
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
int currentWater = w;
int lastPosition = 0;
int refillCount = 0;
int n = position.length;
for (int i = 0; i < n; i++) {
int distanceToNextStation = position[i] - lastPosition;
currentWater -= distanceToNextStation;
// 水量不足进行补给
while (currentWater < 0 && !maxHeap.isEmpty()) {
currentWater += maxHeap.poll();
refillCount++;
}
if (currentWater < 0) {
return -1;
}
lastPosition = position[i];
maxHeap.offer(supply[i]);
}
currentWater -= (d - lastPosition);
while (currentWater < 0 && !maxHeap.isEmpty()) {
currentWater += maxHeap.poll();
refillCount++;
}
if (currentWater < 0) {
return -1;
}
return refillCount;
}
}
关键步骤解析
- 最大堆的使用:在面临补给选择时,我们总是希望选择能够提供最多水量的补给站。最大堆可以帮助我们快速找到当前情况下最优的补给选择,从而减少补给次数。
- 水量检查:在每一步,我们都需要检查当前水量是否足以到达下一个补给站或终点。如果水量不足,则立即进行补给,确保小U能够安全前行。
- 位置更新:每次补给后,我们需要更新当前位置,以便计算到达下一个补给站所需消耗的水量。
- 终点检查:在遍历完所有补给站后,我们还需要检查从最后一个补给站到终点的距离,确保小U能够安全到达终点。
算法优势
- 贪心策略:通过选择最优的补给策略,我们可以在每一步都确保小U的水量供应,从而减少总的补给次数。
- 高效性:使用最大堆进行补给选择,使得算法的时间复杂度保持在较低水平,适用于大规模数据。
复杂度分析
时间复杂度:
- 创建最大堆的时间复杂度为
O(1)
。 - 遍历每个补给站的时间复杂度为
O(n)
,其中n
是补给站数量。 - 对于每个补给站,计算到达下一个补给站所需消耗的水量的时间复杂度为
O(1)
。 - 从最大堆中取出最大水量进行补给的时间复杂度为
O(log n)
,因为堆操作的时间复杂度为O(log n)
。 - 更新当前位置和将补给站的水量加入最大堆的时间复杂度为
O(1)
。
因此,整个算法的时间复杂度主要由遍历补给站和从最大堆中取水的时间复杂度决定,即 O(n log n)
。
空间复杂度:
- 最大堆的空间复杂度为
O(n)
,因为最多需要存储n
个补给站的水量。 - 存储结果的数组
refillCount
的空间复杂度为O(n)
,因为它需要存储每个补给站所需的补给次数。 - 其他变量的空间复杂度为
O(1)
。
综上所述,整个算法的空间复杂度为 O(n)
。