Poj 2823 (单调队列)

题目链接:http://poj.org/problem?id=2823

题意:给出一个数列,长度为n,然后有一个宽度为k的窗口自数列最左端开始滑动至其右边到达数列的最右端,要求出整个过程中,滑动窗口在每个位置内的最大值和最小值。

 

同样是区间最值问题,线段树、RMQ、数状数组可以解决,不过此题有一点不同之处在于,其窗口是连续滑动的,貌似有动态规划的性质:

p[i][k]=max(p[i-1][k-1],a[i]);

复杂度很高,如果能快速求出p[i-1][k-1]就好了,这里有这样一种结构,单调队列:

 

以下摘自某博文:

 

一.       什么是单调(双端)队列

单调队列,顾名思义,就是一个元素单调的队列,那么就能保证队首的元素是最小(最大)的,从而满足动态规划的最优性问题的需求。

单调队列,又名双端队列。双端队列,就是说它不同于一般的队列只能在队首删除、队尾插入,它能够在队首、队尾同时进行删除

 

 

单调队列的性质

一般,在动态规划的过程中,单调队列中每个元素一般存储的是两个值:

1.      在原数列中的位置(下标)

2.      他在动态规划中的状态值

而单调队列则保证这两个值同时单调

 

 

从以上看,单调队列的元素最好用一个类来放,不这样的话,就要开两个数组。。。

 

 

插入方法(以最大单调队列为例):

在插入一个元素前,先判断队列是否为空(head<=tail),然后再判断队尾元素是否比要插入的元素小(q[tail].val<data[insert]),如果是的话,则将队尾元素删除(--tail)。

重复以上过程,直至为空或队尾元素比其大。

 

有了上述结构,就不需要求出p[i][k]( k = 1 ~ K);而是可能通过单调队列的单调性质,只需要删除队列中下标 j<i-k的元素,就可得到p[i];

 

Code:

 

 

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define M 1000008
typedef int Q;
int n,k;
int a[M],r[M];
Q q[M];
int front,tail;

bool Cmpl(int a,int b)
{
	return a>b;
}
bool Cmpr(int a,int b)
{
	return a<b;
}
void Run(bool (*Cmp)(int ,int ))
{
	int i,l;
	front=tail=0;
	for(i=l=0;i<n;i++){
		while(front<tail&&(Cmp)(a[q[tail-1]],a[i]))//此处可以以二分来进行优化
			tail--;
		q[tail++]=i;
		while(i-q[front]>=k) front++;
		r[l++]=a[q[front]];
	}
	for(i=k-1;i<l;i++) printf(i-k+1?" %d":"%d",r[i]);
	puts("");
}

int main()
{
	int i;
	while(~scanf("%d%d",&n,&k)){
		k=k>n?n:k;
		for(i=0;i<n;i++) 
			scanf("%d",a+i);
		Run(Cmpl);
		Run(Cmpr);
	}
	return 0;
}

你可能感兴趣的:(优化,存储,ini,insert)