题目大意
给出一段序列,一个长度一定的窗口从左到右滑动。求窗口滑动到每个位置时窗口内数字的最大值、最小值各是多少。n<=1e6。
总体思路
遇到这种对一个沿着一个方向滑动的区间求最值问题,可以运用单调队列优化。以求最小值为例。对整个序列构造一队列维护元素的下标,供多个窗口重复使用。设窗口左右端点各为l,r。要求对每个[l,r],通过一些操作,使得该队列满足下列条件:
- 元素只属于[l,r]。
- 从左到右下标对应的值必须是递增的。
- 元素∈[l,r]的数量最多。
这样,该队列就会达到以下效果:
- 对于当前区间的最值,队首对应的值便是答案。
- 保证以后的[l+1, r+1]区间要找最值就在这个队列里找,不会漏
这样每次遇到一个r,
- 不断从队首出队直到队首的下标>=l(满足条件1)
- 将对应数大于r对应数的队尾元素从队尾弹出(满足条件2),将r入列(满足条件3).
- 直接输出队首下标所对应数(利用条件2)
#include <cstdio> #include <cstring> #include <deque> #include <cstdarg> using namespace std; #define LOOP(i, n) for(int i=0; i<n; i++) const int MAX_N = 1000010; struct IntDeque { int a[MAX_N], head, tail; void clear() { head = tail = 0; } void push_back(int x) { a[tail++] = x; } void pop_back() { tail--; } void pop_front() { head++; } bool empty() { return head == tail; } int front() { return a[head]; } int back() { return a[tail - 1]; } }; void Proceed(int*val, int n, int k , bool(*opt)(int,int)) { static IntDeque pQ; pQ.clear(); LOOP(curR, n) { int curL = curR - k + 1; while (!pQ.empty() && pQ.front() < curL) pQ.pop_front(); while (!pQ.empty() && opt(val[curR], val[pQ.back()]) ) pQ.pop_back(); pQ.push_back(curR); if (curL >= 0) printf("%d ", val[pQ.front()]); } printf(" "); } bool _lt(int x, int y) { return x < y; } bool _gt(int x, int y) { return x > y; } int main() { #ifdef _DEBUG freopen("c:\noi\source\input.txt", "r", stdin); #endif int n, k; static int val[MAX_N]; memset(val, 0, sizeof(val)); scanf("%d%d", &n, &k); LOOP(i, n) scanf("%d", i + val); Proceed(val, n, k,_lt); Proceed(val, n, k, _gt); return 0; }