写法
首先要有一个双端队列:
struct My_dequeue{
int hh=1,tt=0,q[N];
void clear(){hh=1;tt=0;}
void push_front(int k){q[--hh]=k;}
void push_back(int k){q[++tt]=k;}
void pop_front(){hh++;}
void pop_back(){tt--;}
int front(){return q[hh];}
int back(){return q[tt];}
int head(){return hh;}
int tail(){return tt;}
bool empty(){return hh>tt;}
}q;
以下给出单调队列的标准写法。
int calc(int i){//返回下标对应的值
return inp[i];
}
void insert(int x){//传入下标
while(!q.empty()&&calc(q.back())<=calc(x)) q.pop_back();//维护最大值
q.push_back(x);//存入下标
}
int query(int x){
while(q.front()<=x-k) q.pop_front();//弹出不符合条件的下标
return calc(q.front());//返回值
}
q.clear();
for(int i=1;i<=n;i++){
insert(i);
ans[i]=query(i);
}
作用
维护左右端点单调的若干定长连续区间的 RMQ。
可以用于优化 DP 。要求当前状态的所有值可以从上一个状态的某个连续的段的值得到,将要对这个连续的段进行 RMQ 操作,且相邻状态的段的左右区间满足非降的关系。
具体的说,要求 DP 的状态转移方程满足以下形式( \(b\) 为任意与\(k\)无关的式子,\(x,y\) 为任意与$j $无关的式子):
\[f(i,j)=\max\{f(i-1,k)\}+b,k\in[j-x,j+y] \]\[f(i)=max\{f(k)\}+b,k\in[i-x,i-y],x>0,y>0 \]那么,就可以通过单调队列优化其时间复杂度。
具体的说,我们可以用单调队列维护上一层的 DP 数组的区间最值。由于每一层内求最值的区间长度固定,就可以仿照上面的写法将其构造成一个单调队列,计算新状态时就可以以均摊 \(O(1)\) 的时间复杂度维护最值信息,这样总时间复杂度就可以少一个 \(n\)。是一个优秀的优化。
单调队列优化通常配合滚动数组一起适用,可以让时空均少一个 \(n\)。
例如:
\[f(i)=max\{f(k)\}+a[i],k\in[i-R,i-L] \]那么代码是这样的:
int calc(int i){//返回下标对应的值
return f[i];
}
void insert(int x){//传入下标
while(!q.empty()&&calc(q.back())<=calc(x)) q.pop_back();//维护最大值
q.push_back(x);//存入下标
}
int query(int x){
while(q.front()<=x-R-1) q.pop_front();//弹出不符合条件的下标
return calc(q.front());//返回值
}
q.clear();
for(int i=L;i<=n;i++){
insert(i-L);
f[i]=query(i)+a[i];
if(i+R>n) ans=max(ans,f[i]);
}
标签:return,队列,tt,int,hh,void,单调
From: https://www.cnblogs.com/TKXZ133/p/17456548.html