单调队列单调栈学习来自
这里
Sliding Window
Time Limit: 12000MS | Memory Limit: 65536K | |
Total Submissions: 76927 | Accepted: 21776 | |
Case Time Limit: 5000MS |
Description
An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 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 |
Your task is to determine the maximum and minimum values in the sliding window at each position.
Input
The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.
Output
There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.
Sample Input
<span style="color:#000000">8 3
1 3 -1 -3 5 3 6 7
</span>
Sample Output
<span style="color:#000000">-1 -3 -3 -3 3 3
3 3 5 5 6 7
</span>
Source
题意
给n、m,n个数。在这n个数中,连续为m的序列中最小数输出一行和最大的数输出一行。
单调队列的时间复杂度是O(N),因为每个数只会进队和出队一次,所以这个算法的效率还是很高的。
注意:建议直接用数组模拟单调队列,因为系统自带容器不方便而且不易调试,同时,每个数只会进去一次,所以,数组绝对不会爆,空间也是S(N),优于堆或线段树等数据结构。
更重要的:单调是一种思想,当我们解决问题的时候发现有许多冗杂无用的状态时,我们可以采用单调思想,用单调队列或类似于单调队列的方法去除冗杂状态,保存我们想要的状态,
通过学习上面的博客。若要维护最大值,则要构造单调递减的序列,维护最小值,则要构造单调递增的序列。
我的上篇文章https://blog.csdn.net/qq_41286356/article/details/89554548粗略的写了一下单调栈的思路。
感觉这种做法真的是很妙,很像暴力的方法居然可以这样去弄,因为这需要双端队列的操作,c++自带的queue可能不那么方便,所以可能要自己模拟队列。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
int x,y;
}v[1010000]; //x表示值,y表示位置 可以理解为下标
int a[1010000],n,m,mx[1010000],mn[1010000];
void getmin()
{
int i,head=1,tail=0;// 默认起始位置为1 因为插入是v[++tail]故初始化为0
for(i=1;i<m;i++)
{
while(head<=tail && v[tail].x>=a[i]) tail--;
v[++tail].x=a[i],v[tail].y=i;
// 根据题目 前m-1个先直接进入队列
}
for(;i<=n;i++)
{
while(head<=tail && v[tail].x>=a[i]) tail--;
v[++tail].x=a[i],v[tail].y=i;
while(v[head].y<i-m+1) head++;
mn[i-m+1]=v[head].x;
// 道理同上,当然了 要把已经超出范围的从head开始排出
// 然后每个队首则是目前m个数的最小值
}
}
void getmax() //最大值同最小值的道理,只不过是维护的是递减队列
{
int i,head=1,tail=0;
for(i=1;i<m;i++)
{
while(head<=tail && v[tail].x<=a[i]) tail--;
v[++tail].x=a[i],v[tail].y=i;
}
for(;i<=n;i++)
{
while(head<=tail && v[tail].x<=a[i]) tail--;
v[++tail].x=a[i],v[tail].y=i;
while(v[head].y<i-m+1) head++;
mx[i-m+1]=v[head].x;
}
}
int main()
{
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
getmin();
getmax();
for(i=1;i<=n-m+1;i++)
printf("%d ",mn[i]);
printf("\n");
for(i=1;i<=n-m+1;i++)
printf("%d ",mx[i]);
printf("\n");
return 0;
}