【滑动窗口】单调队列

题目

题意
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
Window position Minimum value Maximum value
[1 3 -1] -3 5 3 6 7 ,最小值:-1,最大值:3
1 [3 -1 -3] 5 3 6 7 ,最小值:-3,最大值:3
1 3 [-1 -3 5] 3 6 7 ,最小值:-3,最大值:5
1 3 -1 [-3 5 3] 6 7 ,最小值:-3,最大值:5
1 3 -1 -3 [5 3 6] 7 ,最小值:3,最大值:6
1 3 -1 -3 5 [3 6 7] ,最小值:3,最大值:7
Input
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
Output
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7

题目大意

本题给出一个长度为 n 的数列和长度为 k 的窗口,使得窗口从左边界开始,每次向右滑动一个数字直到右边界。问每次窗口内的最大值和最小值为多少。

解题思路

本题需要通过记录下标的方式顺序维护一个单调递增队列。首先将 1 ~ k - 1 号元素 push 进队列,如果当前队尾元素大于要放入的元素,则需要弹出队尾元素直到队尾元素小于等于待放入元素。之后从 k 元素开始,每次放入i号元素并且维护窗口的大小,窗口大小只需要判断 i 与队首元素的差有没有超过k,没有超过说明窗口大小合适,同时还要注意满足队列单调性。若窗口大小超过k则需要弹出队首元素,然后将队首元素对应的元素值存放到到 mini[i] 中。
求最大值的方法与求最小值基本一样,只是需要维护一个单调递减的队列,每次将结果存储到maxi[i]中。
本题求解思路会有些难以理解,要注意每次维护队列的过程和确定窗口长度的过程。这里采用队列中存储下标的方式可以使程序更加简洁。另外还有一点需要注意的是本题不能使用常用的 <queue> 头文件,因为 queue 无法双向弹出,需要使用 <deque> 来表示队列。最后对于本题数据量较大,需要采用scanf函数来读入数据,否则会超时。

具体代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<deque>
#define ll long long
#define ld long double
using namespace std;

int maxi[1000005] ,mini[1000000];
int a[1000005];

int main()
{
	int n,k;
	deque<int> q;
	cin >> n >> k;
	for(int i = 0; i < n; i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i = 0; i < k-1; i++)
	{
		while(!q.empty() && a[q.back()] > a[i])
		{
			q.pop_back();
		}
		q.push_back((i));
	}
	for(int i = k - 1; i < n; i++)
	{
		while(!q.empty() && a[q.back()] > a[i])
		{
			q.pop_back();
		}
		q.push_back(i);
		while(!q.empty() && (i - q.front()) >= k)
		{
			q.pop_front();
		}
		mini[i] = a[q.front()];
	}
	deque<int> p;
	for(int i = 0; i < k-1; i++)
	{
		while(!p.empty() && a[p.back()] < a[i])
		{
			p.pop_back();
		}
		p.push_back((i));
	}
	for(int i = k - 1; i < n; i++)
	{
		while(!p.empty() && a[p.back()] < a[i])
		{
			p.pop_back();
		}
		p.push_back(i);
		while(!p.empty() && (i - p.front()) >= k)
		{
			p.pop_front();
		}
		maxi[i] = a[p.front()];
	}
	for(int i = k-1; i < n; i++)
	{
		printf("%d ",mini[i]);
	}
	printf("\n");
	for(int i = k-1; i < n; i++)
	{
		printf("%d ",maxi[i]);
	}
	printf("\n");
    return 0;
}
原创文章 46 获赞 1 访问量 1513

猜你喜欢

转载自blog.csdn.net/weixin_43676449/article/details/104974948
今日推荐