POJ2823 (单调队列)
题意:给n个数字,有一个大小为k的框,从左往右框数字,一共框n-k+1次,问每次框中数字的最大,最小值。
思路:可以先思考只求最大值,因为1e6的数字,维护最大堆的复杂度是nlogn,肯定超时了。我们可以想到,框每次向右移动一格,只会增加一个数字,减少一个数字,可以想到,我们要维护一个序列,
通过这个序列来更新最大值,抹除左边出去的值,这个可以想到要用单调队列来解决,我们维护一个单调递减的序列,每次新加的数字如果最小,放队尾,如果较大,就依次删队尾元素,找到它该在的位置。通过这个方式,我们使一个队列,始终有着目前的最大值。
总之,对于求最大值,维护一个递减序列,求最小值,维护一个递增序列。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+10;
struct note
{
int x,y;
} q[maxn];
int minn[maxn],maxx[maxn];
int a[maxn];
int n,k;
void getmax()
{
int head=1;
int tail=0;
for(int i=1; i<=k-1; i++)
{
while(q[tail].x<=a[i]&&head<=tail)
{
tail--;
}
q[++tail].x=a[i];
q[tail].y=i;
}
for(int i=k; i<=n; i++)
{
while(q[tail].x<=a[i]&&head<=tail)
{
tail--;
}
q[++tail].x=a[i];
q[tail].y=i;
while(q[head].y<(i-k+1))
{
head++;
}
maxx[i-k+1]=q[head].x;
}
}
void getmin()
{
int head=1;
int tail=0;
for(int i=1;i<=k-1;i++)
{
while(a[i]<=q[tail].x&&head<=tail)
{
tail--;
}
q[++tail].x=a[i];q[tail].y=i;
}
for(int i=k;i<=n;i++)
{
while(a[i]<=q[tail].x&&head<=tail)
{
tail--;
}
q[++tail].x=a[i];q[tail].y=i;
while(q[head].y<(i-k+1))
{
head++;
}
minn[i-k+1]=q[head].x;
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
getmax();
getmin();
for(int i=1; i<=n-k+1; i++)
printf("%d ",minn[i]);
printf("\n");
for(int i=1;i<=n-k+1;i++)
printf("%d ",maxx[i]);
}