POJ2823 Sliding Window 单调队列

题目大意

给出一段序列,一个长度一定的窗口从左到右滑动。求窗口滑动到每个位置时窗口内数字的最大值、最小值各是多少。n<=1e6。

总体思路

遇到这种对一个沿着一个方向滑动的区间求最值问题,可以运用单调队列优化。以求最小值为例。对整个序列构造一队列维护元素的下标,供多个窗口重复使用。设窗口左右端点各为l,r。要求对每个[l,r],通过一些操作,使得该队列满足下列条件:

  1. 元素只属于[l,r]。
  2. 从左到右下标对应的值必须是递增的。
  3. 元素∈[l,r]的数量最多。

这样,该队列就会达到以下效果:

  1. 对于当前区间的最值,队首对应的值便是答案。
  2. 保证以后的[l+1, r+1]区间要找最值就在这个队列里找,不会漏

这样每次遇到一个r,

  1. 不断从队首出队直到队首的下标>=l(满足条件1)
  2. 将对应数大于r对应数的队尾元素从队尾弹出(满足条件2),将r入列(满足条件3).
  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;
}

  

  

原文地址:https://www.cnblogs.com/headboy2002/p/8484436.html