徒步旅行中的补给问题
队列
public static int solution(int n, int k, int[] data) {
int minMoney = 0;
Queue<Integer> ready = new LinkedList<>();
int minValue;
for (int i = 0; i < n; i++) {
// 当前站点加入ready
ready.add(data[i]);
// 如果ready大于k,就将最先进入的站点价格删除
if (ready.size() > k) {
ready.poll();
}
// 找到最小值,时间复杂度为O(k)
minValue = findMin(ready);
minMoney += minValue;
}
return minMoney;
}
// 找到队列中的最小值
private static int findMin(Queue<Integer> queue) {
int min = Integer.MAX_VALUE;
for (int value : queue) {
min = Math.min(min, value);
}
return min;
}
优化
可以使用 单调队列(Monotonic Queue) 来优化找到窗口最小值的部分:
单调队列维护窗口内的元素顺序,使得队列的最前端始终是窗口的最小值。
每次加入新元素时,将比它大的队列元素移除,保持队列单调递增。
单调队列
import java.util.Deque;
import java.util.LinkedList;
public class Main {
public static int solution(int n, int k, int[] data) {
int minMoney = 0;
// 单调队列,用于存储当前窗口的索引,队列中存储的数据是单调递增的
Deque<Integer> deque = new LinkedList<>();
for (int i = 0; i < n; i++) {
// 如果队列首部的索引不在窗口范围内,移除它
if (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
deque.pollFirst();
}
// 从队列尾部移除所有比当前元素大的值
// 这样可以保证队列中元素的单调性(从小到大)
while (!deque.isEmpty() && data[deque.peekLast()] > data[i]) {
deque.pollLast();
}
// 将当前索引加入队列
deque.offerLast(i);
// 累加当前窗口的最小值(队列首部存储的是最小值的索引)
minMoney += data[deque.peekFirst()];
}
return minMoney;
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(5, 2, new int[] { 1, 2, 3, 3, 2 }) == 9);
}
}
ps:代码都是GPT给出的,自己尝试用贪心总是有点问题