问题描述
小U正在准备穿越一片广阔的沙漠。沙漠里有多个绿洲,每个绿洲设有补给站。所有补给站的收费相同,但提供的水量不同。从起点到终点共有 (D) 公里,小U需要规划在哪些补给站停留,以保证整个旅途中水的供应。
起点到终点的距离为 (D) 公里,小U初始携带 (W) 升水,每行走 1 公里消耗 1 升水。小U希望通过最少的补给次数安全到达终点。每个补给站的位置由 position[i] 表示,距离起点的公里数,supply[i] 表示该站可以提供的水量,单位为升。
请你帮助小U计算,要安全到达终点至少需要补给几次,或者判断是否根本无法到达终点,此时输出-1。
1. 问题分析
小U需要从起点到终点,确保途中水量充足,目标是最少的补给次数。
已知条件
- 总距离为 DDD 公里。
- 初始携带 WWW 升水。
- 每行走 1 公里消耗 1 升水。
- 每个补给站的位置 position[i]\text{position}[i]position[i] 和提供的水量 supply[i]\text{supply}[i]supply[i]。
目标
计算至少需要补给几次,或者判断是否无法到达终点。
2. 解题思路
(1) 核心问题
- 每次在水量耗尽前找到一个最优补给站,保证后续能够走得更远。
- 如果在某一位置没有可用补给站,并且水量不足以继续前进,则无法到达终点。
(2) 贪心策略
为了尽量减少补给次数,小U每次到达补给站时,应优先选择提供最大水量的站点,以延长续航距离。这是因为选择补给最多的站点可以减少未来的补给需求。
3. 贪心算法的步骤
-
按位置排序
- 按照 position[i]\text{position}[i]position[i] 对补给站排序,保证按前进方向遍历。
- 方便模拟小U的行进过程。
-
初始化状态
- 当前水量为 WWW。
- 当前所在位置为 0。
- 记录补给次数 count=0\text{count} = 0count=0。
- 用一个最大堆(优先队列)来存储当前可用的补给站提供的水量(按降序)。
-
模拟行进
- 遍历所有补给站,依次检查是否可以到达该站。
- 如果当前水量不足以到达下一个站点,从堆中取出最大水量的补给站补给,增加补给次数。
- 如果堆为空且水量不足,则无法到达,输出 -1。
-
到达终点检查
- 模拟到达终点的过程。如果终点前水量不足但仍有补给站可用,继续从堆中补充。
- 如果最终水量仍不足,则无法到达终点。
4. 算法实现
伪代码
python
复制代码
def min_refills(D, W, position, supply):
# 按位置排序补给站
stations = sorted(zip(position, supply))
stations.append((D, 0)) # 添加终点作为虚拟补给站
# 初始化
current_water = W # 当前水量
current_position = 0 # 当前所在位置
count = 0 # 补给次数
max_heap = [] # 最大堆,存储当前可用补给站的水量(负数表示最大堆)
for next_position, next_supply in stations:
distance = next_position - current_position # 到下一个补给站的距离
# 如果水量不足以到达下一个补给站,从堆中补充水量
while current_water < distance:
if not max_heap: # 堆为空,无水可补
return -1
# 从堆中取出最大水量补充
current_water += -heapq.heappop(max_heap)
count += 1
# 减少水量并更新当前位置
current_water -= distance
current_position = next_position
# 将当前补给站的水量加入堆
heapq.heappush(max_heap, -next_supply)
return count
5. 复杂度分析
-
时间复杂度:
- 排序补给站:O(nlogn)O(n \log n)O(nlogn)。
- 遍历补给站并维护堆:O(nlogn)O(n \log n)O(nlogn)。
- 总复杂度为 O(nlogn)O(n \log n)O(nlogn)。
-
空间复杂度:
- 最大堆的空间:O(n)O(n)O(n)。
6. 示例分析
示例 1
plaintext
复制代码
输入:
D = 25
W = 10
position = [10, 15, 20]
supply = [10, 5, 5]
小U正在准备穿越一片广阔的沙漠。沙漠里有多个绿洲,每个绿洲设有补给站。所有补给站的收费相同,但提供的水量不同。从起点到终点共有 (D) 公里,小U需要规划在哪些补给站停留,以保证整个旅途中水的供应。
起点到终点的距离为 (D) 公里,小U初始携带 (W) 升水,每行走 1 公里消耗 1 升水。小U希望通过最少的补给次数安全到达终点。每个补给站的位置由 `position[i]` 表示,距离起点的公里数,`supply[i]` 表示该站可以提供的水量,单位为升。
请你帮助小U计算,要安全到达终点至少需要补给几次,或者判断是否根本无法到达终点,此时输出-1。
输出:
1
解释
- 初始水量为 10,可以到达第一个补给站(位置 10)。
- 在第一个补给站补充 10 升水,总水量变为 20。
- 剩余路程为 15 公里,水量足够到达终点,补给次数为 1。
示例 2
plaintext
复制代码
输入:
D = 25
W = 10
position = [10, 15]
supply = [5, 5]
输出:
-1
解释
- 初始水量为 10,可以到达第一个补给站。
- 第一个补给站补充 5 升水,总水量为 15。
- 到第二个补给站后,总水量不足以到达终点,无法完成旅程。
7. 总结
- 贪心算法每次选择最大补给量的站点,保证最优解。
- 边界情况如无法到达终点时,及时退出返回 -1。