您的位置:首页 > 其它

POJ[2823]窗口 单调队列

2016-09-02 19:53 363 查看
题目地址poj.org/problem?id=2823

题目大意:

给你一个长度为N的数组,一个长为K滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表:

Window position

Min value

Max 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

你的任务是找出窗口在各位置时的Max value ,Min value。

输入格式:

第一行n,k,第二行长度为n的数组

输出格式:

第一行每个位置的Min value,第二行每个位置的Max value

样例:

Window.in

8 3

1 3 -1 -3 5 3 6 7

Window.out

-1 -3 -3 -3 3 3

3 3 5 5 6 7

数据范围:

20%:n≤500;

50%:n≤100000;

100%:n≤1000000;

时间限制:

12000ms

RMQ问题,线段树代码量太大于是用单调队列实现- -

单调队列具有队列内所有元素不是单调递增就是单调递减的性质,所以每次的最小(最大)值一定会在队首

程序实现过程中先将前k个元素入队,此后每次在队尾加入a[k+1...n],在插入元素中同时进行以下操作:

1、将队尾所有大于a[i]的值弹出队列

2、插入a[i]到队尾

3、判断队首元素位置是否超出i-k

注意:

1、在更新队列元素时要同时记录该元素在原数据的位置

2、在进行操作1时要用二分优化(可以用C++编译器卡时间AC,但换成G++和GCC就会TLE)



代码如下:

#include<cstring>
#include<cstdio>
#define N 1000050
#define INF 2147483647
using namespace std;
int n,k;
int a
;
struct Elem{
int k,num;
}Queue
;
int l=1,r=1;
inline void GetMin(){
memset(Queue,0,sizeof Queue);
Queue[0].k=-INF;
l=1,r=1;
for(int i=1;i<=k;i++){
while(Queue[r].k>=a[i] && r>=l) r--;
Queue[++r].k=a[i];
Queue[r].num=i;
}
for(int i=k;i<=n;i++){
while(Queue[r].k>=a[i] && r>=l) r--; //维护单调性
Queue[++r].k=a[i];
Queue[r].num=i;
while(Queue[l].num<=i-k) l++;  //维护队列下标范围k以内
printf("%d ",Queue[l].k);
}
}
inline void GetMax(){
memset(Queue,0,sizeof Queue);
Queue[0].k=INF;
l=1,r=1;
for(int i=1;i<=k;i++){
while(Queue[r].k<=a[i] && r>=l) r--;
Queue[++r].k=a[i];
Queue[r].num=i;
}
for(int i=k;i<=n;i++){
while(Queue[r].k<=a[i] && r>=l) r--;
Queue[++r].k=a[i];
Queue[r].num=i;
while(Queue[l].num<=i-k) l++;
printf("%d ",Queue[l].k);
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
GetMin();
printf("\n");
GetMax();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: