D - 滑动窗口(单调队列)

D - 滑动窗口

  • 题意:
    ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
    数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
    D - 滑动窗口(单调队列)_第1张图片

  • 输入输出:
    Input
    输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
    Output
    输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
    D - 滑动窗口(单调队列)_第2张图片

  • 解题思路:
    本题为典型的单调队列应用,用stl中的deque可以实现从两端的入队和出队。定义两个数组maxx和minn分别记录每个窗口的最大和最小值,定义队列q,先对第一次的窗口进行初始化,以递增队列举例。在规定长度内,若队列为空或即将入队的元素大于队尾元素,符合单调队列定义,元素入队。否则弹出队尾元素,直到队尾元素小于即将入队元素。

    由于是递增队列,故每次的队首元素即为当前窗口内的最小元素,存入minn数组。因为滑动窗口的长度有规定,为了保证不超出范围,需要计算每次滑动后队尾与队首元素的差值,因为单调队列中存储的是元素的编号,若差值大于规定长度,用pop_front()弹出队首元素。

    注意:本体数据量较大,需要使用scanf和printf进行输入输出,否则会超时。

  • 代码实现:

#include 
#include 
#include 
using namespace std;

int minn[1000010],maxx[1000010],a[1000010],n,limit;
deque <int> q;

void mindeque()
{
	q.clear();
	for (int i=0;i<limit-1;i++)//初始化窗口 
	{
		while(!q.empty()&&a[q.back()]>a[i])
			q.pop_back();
		q.push_back(i);
	}
	for (int i=limit-1;i<n;i++) 
	{
		while(!q.empty()&&a[q.back()]>a[i])
			q.pop_back();
		q.push_back(i);
		while(!q.empty()&&q.back()-q.front()+1>limit)
			q.pop_front();
		minn[i]=a[q.front()];
	}
	return;
}

void maxdeque()
{
	q.clear();
	for (int i=0;i<limit-1;i++)//初始化窗口 
	{
		while(!q.empty()&&a[q.back()]<a[i])
			q.pop_back();
		q.push_back(i);
	}
	for (int i=limit-1;i<n;i++) 
	{
		while(!q.empty()&&a[q.back()]<a[i])
			q.pop_back();
		q.push_back(i);
		while(!q.empty()&&q.back()-q.front()+1>limit)
			q.pop_front();
		maxx[i]=a[q.front()];
	}
	return;
}

int main()
{
	scanf("%d%d",&n,&limit);
	for (int i=0;i<n;i++)
		scanf("%d",&a[i]);
		
	mindeque();
	maxdeque();
	
	for (int i=limit-1;i<n-1;i++)
		printf("%d ",minn[i]);
	printf("%d\n",minn[n-1]);
	for (int i=limit-1;i<n-1;i++)
		printf("%d ",maxx[i]);
	printf("%d\n",maxx[n-1]);
	return 0;
	
}

你可能感兴趣的:(D - 滑动窗口(单调队列))