Time Limit: 12000MS | Memory Limit: 65536K | |
Total Submissions: 50163 | Accepted: 14456 | |
Case Time Limit: 5000MS |
Description
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
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
Source
给定你n个数ai~an一个确定长度的区间,让你求出每个区间内的最大值,并按照顺序输出
输入
N,k
A1~an
输出
每个区间内的最大值
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int queue[1000003];
int n,k,i,j,a[1000003];
int head1,tail1,head2,tail2;
inline bool scan_d(int &num) //快速读入
{
char in;bool IsN=false;
in=getchar();
if(in==EOF)
return false;
while(in!='-'&&(in<'0'||in>'9')) in=getchar();
if(in=='-') { IsN=true;num=0;}
else num=in-'0';
while(in=getchar(),in>='0'&&in<='9')
{
num*=10,num+=in-'0';
}
if(IsN)
num=-num;
return true;
}
int main()
{
scanf("%d%d\n",&n,&k);
for (i=1;i<=n;i++)
scan_d(a[i]);
if (k==1)
{
for (i=1;i<=n;i++)
printf("%d ",a[i]);
printf("\n");
for (i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
tail1=1; queue[1]=1; head1=1;
for (i=2;i<=n;i++)
{
while (queue[head1]<i-k+1&&head1<=tail1)
head1++;
while (a[queue[tail1]]>a[i]&&tail1>=head1)
tail1--;
tail1++; queue[tail1]=i;
if (i>=k)
printf("%d ",a[queue[head1]]);
}
printf("\n");
memset(queue,0,sizeof(queue));
tail1=1; queue[1]=1; head1=1;
for (i=2;i<=n;i++)
{
while (queue[head1]<i-k+1&&head1<=tail1)
head1++;
while (a[queue[tail1]]<a[i]&&tail1>=head1)
tail1--;
tail1++; queue[tail1]=i;
if (i>=k)
printf("%d ",a[queue[head1]]);
}
}
//这题首先想到的是线段树,但是每次都查询时间复杂度太高。
于是想到单调队列,所谓单调队列就是保持单调性的队列,单调队列又称双端队列,即既可以从尾出队,又可以从头出队。以此题为例,我们保证队首元素最大(或最小),因为有范围限制,那么如果队列的头元素的下标已经超出了(i-k+1),那么我们就可以出队。然后把当前元素从尾端入队,只要队尾元素比当前元素小(或大),那么队尾元素出队,因为我们要维护的是最值,所以队尾元素直接出队即可,不会影响最终结果。有一点需要特别注意,那就是队首与队尾的范围,最少的情况是队中只有一个元素,即head==tail.