POJ2823 滑动窗口 (单调队列)
阅读原文时间:2023年07月08日阅读:3

来学习一下单调队列:

他只可以从队尾入队,但可以从队尾或队首出队,来维护队列的单调性。单调队列有两种单调性:元素的值单调和元素的下标单调。

单调队列可以用来优化DP。状态转移方程形如dp[i]=min{dp[j]+f[j]},i-a<=j<=i-b。i增加1,j的上下界都增加1,即加入一个新的决策到候选集中,要把过时的决策剔除。当决策的取值范围的上下界均单调变化时,每个决策在候选集合中插入或删除最多一次,我们就可以用单调队列来优化。

回到这个题目:

用Min[i]和Max[i]表示以i结尾的大小为k的窗口中的最值。

以Min[i]为例,Min[i]=min{aj} ,i-k+1<=j<=i; i加1,j的上下界同时加1。Min用单调递增的队列维护。

(1)单调递增的队列,队头元素最小;

(2)待入队的元素<=队尾元素,队尾元素出队,直到满足单调性或队列为空,将此元素入队;

(3)队头元素下标<i-k+1,说明过时了,要删去。

1 #include
2 using namespace std;
3 #define MAXN 1000010
4 int a[MAXN],Min[MAXN],Max[MAXN],Q[MAXN],n,k;
5
6 void get_min(){//求最小
7 int st=0,ed=0;
8 Q[ed++]=1;
9 Min[1]=a[1];
10 for(int i=2;i<=n;i++){ 11 while(sta[Q[ed-1]]) ed--;
24 Q[ed++]=i;
25 while(st<ed&&Q[st]<i-k+1) st++;
26 Max[i]=a[Q[st]];
27 }
28 }
29
30 int main(){
31 scanf("%d%d",&n,&k);
32 for(int i=1;i<=n;i++) scanf("%d",&a[i]);
33 get_min();get_max();
34 for(int i=k;i<n;i++) cout<<Min[i]<<" "; cout<<Min[n]<<endl;
35 for(int i=k;i<n;i++) cout<<Max[i]<<" "; cout<<Max[n]<<endl;
36 return 0;
37 }