单调队列入门题目
先附上例题解析:
最大队列保证队列中各个元素大小单调递减(即,最大元素在对头,最小的在队尾),同时每个元素的下标单调递增(按校标递增的顺序添加这点可以不用考虑)。这样便保证队首元素最大,而且更新的时候队首永远是当前最大。因此,这个队列需要在两头都可以进行删除,在队尾插入。
维护方法:在每次插入的时候,先判断队尾元素,如果不比待插入元素大就删除,不断删除队尾直到队尾元素大于待插入元素或者队空。删除的时候,判断队首,如果队首元素下标小于当前段左边界就删除,不断删除队首直到队首元素下标大于等于当前段左边界(注意:这时队列肯定不为空),队首元素就是当前段的最优解。
个人补充:
由于扫描的是n这个序列每一个连续的区间为k的最大最小值,所以,由单调队列的性质,输入的第i个值大于k的时候就要开始扫描了。如果处理完所有的输入数据之后才开始扫描的话,有些区间的元素可能会不存在于单调队列里面。
唔……代码有点冗长的说
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxx = 1e6;
struct po
{
int sign;
int val;
po(int v=0, int s=0):sign(s),val(v) {}
} inc[maxx],dre[maxx];
int ans1[maxx];//k区间内最大值
int ans2[maxx];//k区间内最小值
int main()
{
// freopen("2823.txt","r",stdin);
int head_inc,head_dre,tail_inc,tail_dre;
int n,k;
while(scanf("%d%d",&n,&k)!=EOF)
{
memset(ans1,0,sizeof(ans1));
memset(ans2,0,sizeof(ans2));
head_dre = head_inc = tail_dre = tail_inc = 1;
for(int i=1; i<=n; i++)
{
int tem;
scanf("%d",&tem);
if(i==1)//算是初始化吧
{
inc[tail_inc] = po(tem,i);
dre[tail_dre] = po(tem,i);
}
else//注意tail_inc 和 tail_dre 的值的变化
{
for(int j=tail_inc; j>=head_inc; j--)
{
if(tail_inc==head_inc && tem>inc[j].val)
{
inc[tail_inc] = po(tem,i);
break;
}
else if(tem>inc[j].val)
tail_inc--;
else
{
inc[++tail_inc] = po(tem,i);
break;
}
}
for(int j=tail_dre; j>=head_dre; j--)
{
if(tail_dre==head_dre && tem<dre[j].val)
{
dre[tail_dre] = po(tem,i);
break;
}
else if(tem<dre[j].val)
tail_dre--;
else
{
dre[++tail_dre] = po(tem,i);
break;
}
}
}
if(i>=k)
{
for(int j = head_inc; j<=tail_inc; j++)
if(inc[j].sign>=i-k+1 && inc[j].sign<=i)
{
ans1[i-k] = inc[j].val;
break;
}
else
head_inc++;
for(int j = head_dre; j<=tail_dre; j++)
if(dre[j].sign>=i-k+1 && dre[j].sign<=i)
{
ans2[i-k] = dre[j].val;
break;
}
else
head_dre++;
}
}
int vis = 0;
for(int i=0; i<=n-k; i++)
if(vis==0)
{
printf("%d",ans2[i]);
vis = 1;
}
else
printf(" %d",ans2[i]);
printf("\n");
vis = 0;
for(int i=0; i<=n-k; i++)
{
if(vis==0)
{
printf("%d",ans1[i]);
vis = 1;
}
else
printf(" %d",ans1[i]);
}
printf("\n");
}
return 0;
}
额…后来看了一下别人的代码……果然精简多了
//Name: pku_2823_Sliding_Window
//Author: longxiaozhi
//Tag: 单调队列
//Root: 最大单调队列+ 最小单调队列。
#include <iostream>
using namespace std;
const int inf = -1u>>1;
const int maxn = 1e6;
struct node
{
int val, tag;
node(int v=0, int t=0):val(v),tag(t) {} void pt()
{
printf("%d %d\n", val, tag);
}
} q[2][maxn+1];
int h1, r1, h2, r2;
int key[maxn+1], n, k;
int a[maxn+1], b[maxn+1];
int main()
{
freopen("in.in", "r", stdin);
q[0][0] = node(-inf, 0);
q[1][0] = node(+inf, 0);
int i;
while(scanf("%d %d", &n, &k) != EOF)
{
if(k > n) k = n;
for(i = 1; i <= n; i++) scanf("%d", key+i);
h1 = h2 = 1;
r1 = r2 = 0;
for(i = 1; i < k; i++)
{
while(h1 <= r1 && q[0][r1].val < key[i]) r1--;
q[0][++r1] = node(key[i], i);
while(h2 <= r2 && q[1][r2].val > key[i]) r2--;
q[1][++r2] = node(key[i], i);
}
for(i = k; i <= n; i++)
{
while(h1 <= r1 && q[0][r1].val < key[i]) r1--;
q[0][++r1] = node(key[i], i);
while(h1 <= r1 && q[0][h1].tag <= i-k) ++h1;
a[i-k] = q[0][h1].val;
while(h2 <= r2 && q[1][r2].val > key[i]) r2--;
q[1][++r2] = node(key[i], i);
while(h2 <= r2 && q[1][h2].tag <= i-k) ++h2;
b[i-k] = q[1][h2].val;
}
for(i = 0; i < n-k; i++) printf("%d ", b[i]);
printf("%d\n", b[i]);
for(i = 0; i < n-k; i++) printf("%d ", a[i]);
printf("%d\n", a[i]);
}
return 0;
}