单调递增 deque 保存窗口内仍可能成为最小值的下标。
OJ: luogu
题目 ID: P2251
难度:普及
标签:单调队列滑动窗口dequepython
日期: 2026-07-16 21:00
题意
输出每个固定宽度窗口的最小值。
思路
每个窗口都重新扫一遍找最小值,慢在哪里?
最坏
能不能让每个元素只被"看见"有限次?
窗口滑动是确定的顺序过程。设
如何组织"仍可能成为答案"的候选?
候选按下标递增排列;由于上面的淘汰规则,候选的值必须严格递增(出现不增就会淘汰前者)。所以维护一个"下标递增、值严格递增"的队列:新元素入队前,先把队尾所有值
队首什么时候作废?
窗口的候选必须都在窗口内。当前右端是
什么时候开始输出?
第
队列保存下标且对应值严格递增。加入新值时,队尾不小于它的元素以后不可能成为最小值,全部弹出;队首若离开窗口也弹出。形成完整窗口后,队首值就是答案。
Python 知识
collections.deque支持两端删除。 - 队列存下标而非值,才能判断元素是否过期。
enumerate(values)同步取得窗口右端和新值。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, m;
int a[MAXN];
int q[MAXN]; // 单调队列里存下标
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int head = 1;
int tail = 0;
for (int i = 1; i <= n; i++) {
// 把已经滑出窗口左端的下标弹掉。
while (head <= tail && q[head] <= i - m) {
head++;
}
// 维护队列中对应的值单调递增,这样队头就是窗口最小值。
while (head <= tail && a[q[tail]] >= a[i]) {
tail--;
}
q[++tail] = i;
if (i >= m) {
cout << a[q[head]] << '\n';
}
}
return 0;
}python
import sys
from collections import deque
data = iter(map(int, sys.stdin.buffer.read().split()))
n, width = next(data), next(data)
values = [next(data) for _ in range(n)]
queue = deque()
answers = []
for i, value in enumerate(values):
while queue and values[queue[-1]] >= value:
queue.pop()
queue.append(i)
if queue[0] <= i - width:
queue.popleft()
if i + 1 >= width:
answers.append(str(values[queue[0]]))
print("\n".join(answers))复杂度
每个下标进出队各一次,时间
总结
固定窗口最值的标准结构是单调队列,不需要堆的懒删除和对数因子。