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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章