洛谷 P1886 滑动窗口
单调队列有两个性质
就拿样例来谈谈,设以最小的为标准。
8 3 1 3 -1 -3 5 3 6 7
下文中我们用q来表示单调队列,p来表示其所对应的在原列表里的序号。
- 由于此时队中没有一个元素,我们直接令1进队。此时,q={1},p={1}。
- 现在3面临着抉择。下面基于这样一个思想:假如把3放进去,如果后面2个数都比它大,那么3在其有生之年就有可能成为最小的。此时,q={1,3},p={1,2}
- 下面出现了-1。队尾元素3比-1大,那么意味着只要-1进队,那么3在其有生之年必定成为不了最小值,原因很明显:因为当下面3被框起来,那么-1也一定被框起来,所以3永远不能当最小值。所以,3从队尾出队。同理,1从队尾出队。最后-1进队,此时q={-1},p={3}
- 出现-3,同上面分析,-1>-3,-1从队尾出队,-3从队尾进队。q={-3},p={4}。
- 出现5,因为5>-3,同第二条分析,5在有生之年还是有希望的,所以5进队。此时,q={-3,5},p={4,5}
- 出现3。3先与队尾的5比较,3<5,按照第3条的分析,5从队尾出队。3再与-3比较,同第二条分析,3进队。此时,q={-3,3},p={4,6}
- 出现6。6与3比较,因为3<6,所以3不必出队。由于3以前元素都<3,所以不必再比较,6进队。因为-3此时已经在滑动窗口之外,所以-3从队首出队。此时,q={3,6},p={6,7}
- 出现7。队尾元素6小于7,7进队。此时,q={3,6,7},p={6,7,8}。
咳咳,以上引自洛谷题解区dalao hankeke
单调队列的舞台
由于单调队列的队头每次一定最小值,故查询为O(1)。
进队出队稍微复杂点:
进队时,将进队的元素为e,从队尾往前扫描,直到找到一个不大于e的元素d,将e放在d之后,舍弃e之后的所有元素;如果没有找到这样一个d,则将e放在队头(此时队列里只有这一个元素)。
出队时,将出队的元素为e,从队头向后扫描,直到找到一个元素f比e后进队,舍弃f之前所有的。(实际操作中,由于是按序逐个出队,所以每次只需要出队只需要比较队头)。
每个元素最多进队一次,出队一次,摊排分析下来仍然是 O(1)。
引自 单调队列_360百科
所以我就负责上代码啦
q1单调递增
每次放元素先把比它大的弹掉 出了窗口的弹掉
q2相反
//手写队列
#include <iostream>
#include <cstdio>
using namespace std;
int n,k;
int ans[1000005];
struct T
{
int i;
int val;
}tmp;
inline int read()
{
char c;
int x=0,f=1;
do{
c=getchar();
if(c=='-')
f=-1;
}while(c<'0' || c>'9');
do{
x=x*10+c-'0';
c=getchar();
}while(c>='0' && c<='9');
return f*x;
}
struct qwq
{
int h,t;
T q[1000005];
inline void push_back(T x)
{
q[++t]=x;
}
inline void push_front(T x)
{
q[--h]=x;
}
inline void pop_back()
{
t--;
}
inline void pop_front()
{
h++;
}
inline T back()
{
return q[t];
}
inline T front()
{
return q[h];
}
inline bool empty()
{
return h>t;
}
}q1,q2;
int main(){
q1.h=1,q1.t=0;
q2.h=1,q2.t=0;
n=read(),k=read();
for(int i=0;i<n;++i)
{
tmp.i=i;
tmp.val=read();
while(!q1.empty() && q1.back().val>=tmp.val)
q1.pop_back();
while(!q1.empty() && q1.front().i<=i-k)
q1.pop_front();
q1.push_back(tmp);
while(!q2.empty() && q2.back().val<=tmp.val)
q2.pop_back();
while(!q2.empty() && q2.front().i<=i-k)
q2.pop_front();
q2.push_back(tmp);
if(i>=k-1)
{
printf("%d%c",q1.front().val,i==n-1?'\n':' ');
ans[i]=q2.front().val;
}
}
for(int i=k-1;i<n;++i)
{
printf("%d%c",ans[i],i==n-1?'\n':' ');
}
return 0;
}
//deque双向队列
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
int n,k;
int ans[1000005];
struct T
{
int i;
int val;
}tmp;
inline int read()
{
char c;
int x=0,f=1;
do{
c=getchar();
if(c=='-')
f=-1;
}while(c<'0' || c>'9');
do{
x=x*10+c-'0';
c=getchar();
}while(c>='0' && c<='9');
return f*x;
}
deque <T> q1,q2;
int main(){
n=read(),k=read();
for(int i=0;i<n;++i)
{
tmp.i=i;
tmp.val=read();
while(!q1.empty() && q1.back().val>=tmp.val)
q1.pop_back();
while(!q1.empty() && q1.front().i<=i-k)
q1.pop_front();
q1.push_back(tmp);
while(!q2.empty() && q2.back().val<=tmp.val)
q2.pop_back();
while(!q2.empty() && q2.front().i<=i-k)
q2.pop_front();
q2.push_back(tmp);
if(i>=k-1)
{
printf("%d%c",q1.front().val,i==n-1?'\n':' ');
ans[i]=q2.front().val;
}
}
for(int i=k-1;i<n;++i)
{
printf("%d%c",ans[i],i==n-1?'\n':' ');
}
return 0;
}
emmmm…
附deque用法一波 又是抄dalao的
- d[i]:返回d中下标为I的元素的引用。
- d.front():返回的一个元素的引用。
- d.back():返回最后一个元素的引用。
- d.pop_back():删除尾部的元素。不返回值。
- d.pop_front():删除头部元素。不返回值。
- d.push_back(e):在队尾添加一个元素e。
- d.push_front(e):在对头添加一个元素e。