POJ[2823]窗口 单调队列

题目地址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
#include
#define N 1000050
#define INF 2147483647
using namespace std;
int n,k;
int a[N];
struct Elem{
	int k,num;
}Queue[N];
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;
}


你可能感兴趣的:(其他题库,单调队列)