單調佇列 (POJ2823)
單調佇列是從數列前掃到數列後...維護一個最值或者一個所需的最優解之類的...每次的最優解都是在佇列的頭....所以要一直維護佇列..使其從頭到尾都是單調的..要能保證如果當前頭要出去了...後面的元素能馬上頂上來作為頭...
就拿POJ2823來舉例....題目要求是給了一串n個數...從左到右每次框k個連續數..問每次框的數中最大數和最小數是什麼..樣例輸入輸出:
Sample Input
8 3 1 3
-1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7
就拿樣例說....準備一個佇列...Myqueue...h代表隊列頭( 裡面存的是下標不是數..為了是判斷隊首出列)..p代表隊列尾..初始值h=1,p=0...就拿每次要一段最小值的佇列變化過程來描述一下:
1、插入第1個數 Myqueue = { 1 } h=1; p=1 小於所給的框..還不需輸出
2、插入第2個數 Myqueue= { 1 2 } h=1; p=2 ( 因為2號元素比1號元素大...先排到後面 ) 小於所給的框..還不需輸出
3、插入第3個數 Myqueue= { 3 } h=1; p=1; ( 因為3號元素為-1比1號元素的1和2號元素的3都要小~~所以一直擠到最前面...1,2都出列 ) 輸出 a[ Myqueue[h] ]的值 -1
4 、插入第4個數 Myqueue={ 4 } h=1; p=1; ( 因為4號元素比3號元素小....所以擠掉3號元素 ) 輸出 a[ Myqueue[h] ]的值 -3
5、插入第5個數 Myqueue={ 4 5 } h=1; p=2 ( 因為5號元素比4號元素大...先排到後面 ) 輸出 a[ Myqueue[h] ]的值 -3
6、插入第6個數 Myqueue={4 6 } h=1; p=2 ( 因為6號元素比5號元素小....但是又沒有4號元素小..所以只能擠掉5號元素 ) 輸出 a[ Myqueue[h] ]的值 -3
7、插入第7個數 Myqueue={ 6 7 } h=2; p=3 { 這裡為什麼4出列了?因為4到7已經不能被所給的範圍框住了...所以4要出列..然後繼續操作...因為7號元素比6號元素大...先排到後面) 輸出 a[ Myqueue[h] ]的值 3
8、插入第8個數 Myqueue={6 7 8 } h=2; p=4; { 因為8號元素比7號元素和6號元素都要大...所以放到後面... ) 輸出 a[ Myqueue[h] ]的值 3
還有一點要特別注意!!!如果後面的數和h[p]元素的數相等時...也要擠掉h[p]的....因為這個我WA了一次...囧....
這道題用單調佇列來跑只要5000MS..的確快了不少..但看大牛們還能更快...想知道怎麼做到的...
PS:...有個地方一定要注意...每次插入新數到佇列都要從隊尾往隊首掃..掃到比自己小的就確定位置..掃到比自大的就把這個大的數給擠掉.....如果從前往後掃....例如
1 2 3 4 5 6 7 8 9 ..... 很長一列...要插入一個很大的數...從前一直遍歷到最後面..才確定位置..這不是關鍵..關鍵是這一路過去..什麼操作都沒有做..白白掃了一大片空間...而從後往前掃...要麼確定位置...要麼踢到元素...總是在操作..所以必須要從後往前來判斷並插入....
Program :
[cpp] view plaincopyprint?- /*
- POJ2823 - 單調佇列
- */
- #include<iostream>
- usingnamespace std;
- int n,m,a[1000001],Myqueue[1000001];
- void MinQueue()
- {
- int p=0,h=1,k,i;
- Myqueue[1]=1;
- for (k=1;k<=n;k++)
- {
- if (k-Myqueue[h]==m) h++;
- if (p==h-1 || a[k]>a[Myqueue[p]]){ p++; Myqueue[p]=k; }
- else
- {
- while (p>=h && a[k]<=a[Myqueue[p]]) { Myqueue[p]=k; p--; }
- p++;
- }
- if (k>=m) printf("%d ",a[Myqueue[h]]);
- }
- printf("\n");
- }
- void MaxQueue()
- {
- int p=0,h=1,k,i; Myqueue[1]=1;
- for (k=1;k<=n;k++)
- {
- if (k-Myqueue[h]==m) h++;
- if (p==h-1 || a[k]<a[Myqueue[p]]){ p++; Myqueue[p]=k; }
- else
- {
- while (p>=h && a[k]>=a[Myqueue[p]]) { Myqueue[p]=k; p--; }
- p++;
- }
- if (k>=m) printf("%d ",a[Myqueue[h]]);
- }
- printf("\n");
- }
- int main()
- {
- freopen("2823.in","r",stdin);
- freopen("2823T.out","w",stdout);
- scanf("%d%d",&n,&m);
- memset(a,0,sizeof(a));
- for (int i=1;i<=n;i++) scanf("%d",&a[i]);
- MinQueue();
- MaxQueue();
- return 0;
- }