洛谷題目鏈接:滑動窗口
題目描述
現在有一堆數字共N個數字(N<=10^6),以及一個大小為k的窗口。現在這個從左邊開始向右滑動,每次滑動一個單位,求出每次滑動后窗口中的最大值和最小值。
例如:
The array is [1 3 -1 -3 5 3 6 7], and k = 3.
輸入輸出格式
輸入格式:
輸入一共有兩行,第一行為n,k。
第二行為n個數(小於INT_MAX).
輸出格式:
輸出共兩行,第一行為每次窗口滑動的最小值
第二行為每次窗口滑動的最大值
輸入輸出樣例
輸入樣例#1:
8 3
1 3 -1 -3 5 3 6 7
輸出樣例#1:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
說明
50%的數據,n<=10^5
100%的數據,n<=10^6
解釋一下題意:給出一個長度為N的序列,以及一個滑動窗口的長度k.要求出每一次移動后當前位置滑動窗口上的最大值和最小值.
看這個數據范圍,用數據結構什么的是很難過的,很可能被卡常或者是爆空間什么的.所以這里提供了一種神奇的做法:
單調隊列
我們來模擬一下樣例的序列(求滑動窗口最大值):1 3 -1 -3 5 3 6 7. * 首先是1入隊. * 然后3入隊判斷,3比1大,而1又在3前面,所以1比3會先出隊,意味着1在有生之年是當不成最大值了,所以1可以出隊了.此時最大值是隊首3. * 接着-1入隊判斷.-1比3小,說明如果3出隊了-1還是有可能成為最大值的.所以-1直接入隊.最大值仍是隊首3. * -3入隊判斷,比-1小,仍有可能成為最大值,先入隊.此時最大值依舊是隊首3. * 5入隊判斷,比隊尾大,說明之前入隊的-3,-1,3都不可能成為最大值了.於是把它們都出隊.把5入隊,此時最大值是隊首5. * 3入隊同理. * 6入隊,彈出3,5.最大值是6. * 7入隊,彈出6,最大值是7.
那么我們大概就得出了優先隊列的維護方法: * 每個元素入隊前判斷它之前的元素是否可能成為最值.將不可能成為最值的元素都彈出隊. * 將該元素入隊. * 統計當前狀態下隊列的元素個數,從隊首開始彈出一定數量元素保證隊列中元素個數在滑動窗口大小內. * 此時隊首的值就是維護的最值.
具體在操作過程中可以用兩個數組模擬隊列,一個記錄元素的值來判斷是否可能成為最值.另一個記錄元素下標來判斷當前隊列元素個數.
下面看一下代碼注釋:
#include<bits/stdc++.h>
using namespace std;
const int N=1000000+5;
const int inf=2147483647;
int n, k, w[N];
int q1[N], q2[N];//q1統計元素的值,q2統計下標.
int gi(){//讀入優化
int ans = 0 , f = 1; char i = getchar();
while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
return ans * f;
}
int main(){
int h, t; n = gi(); k = gi();
for(int i=1;i<=n;i++) w[i] = gi();
h = 1; t = 0;
for(int i=1;i<=n;i++){
while(h <= t && q1[t] >= w[i]) t--;//如果隊列中有元素且之前的元素都不可能成為要統計的最值,那么直接將該元素彈出.
q1[++t] = w[i]; q2[t] = i;//入隊.
if(i-k >= q2[h]) h++;//如果當前隊列的大小超過了滑動窗口限定的大小,則從隊首開始彈出元素.
if(i >= k) printf("%d ",q1[h]);//此時隊首統計的就是當前滑動窗口的最值.
}
printf("\n"); h = 1; t = 0;//
for(int i=1;i<=n;i++){
while(h <= t && q1[t] <= w[i]) t--;
q1[++t] = w[i]; q2[t] = i;
if(i-k >= q2[h]) h++;
if(i >= k) printf("%d ",q1[h]);
}
printf("\n");
return 0;
}