单调队列

来源:互联网 发布:软件安装app 编辑:程序博客网 时间:2024/04/28 22:41

单调队列


前置技能点:

队列

问题:

对于一个长度为n的序列a,我们希望知道所有以k为长度的子序列的区间最小/大值。

struct Node{    int id;    int v;//value}a[NodeNum];//a[i]的大小由成员变量v决定

思路:

可以以O(k)的复杂度暴力枚举得到minl+k1i=la[i],由于a中长度为k的子序列共有nk+1个,故复杂度为O(n2)

可以通过线段树进行处理,从而减小查询任意一个区间的最值的复杂度,建树复杂度为O(nlogn),查询复杂度为O(logn)

还可以将a序列压入一个按v排序的堆,每次区间整体右移一格,由[l,l+k)变为[l+1,l+k]时将a[l+k]元素压入堆。然后循环判定堆顶元素,若堆顶元素的下标小于等于l则删除,最后堆顶的元素就是minl+ki=l+1a[i],堆的插入删除复杂度均为O(logn)

概念引入:

(minl+k1i=la[i]).id=x(minl+k1i=l+1a[i]).id=x
x==l,则x>x
否则x==x

(minl+k1i=l+1a[i]).id=y
a[l+k].v<=a[y].v,则minl+ki=l+1a[i]).id==l+k
否则minl+ki=l+1a[i]).id==y

那么就可以维护一个单调递增的队列,用于存储当前区间的最小元素,以及当前区间内队列中每一个元素右边的子序列中的最小元素。

当求解minl+k1i=la[i]
queue[head].id==l1,则可以将它弹出队列。
同时将a[l+k1].vqueue[tail].v进行大小判定,若queue[tail].v>=a[l+k1].v则将队尾删除,循环判定直到队列为空或queue[tail].v<a[l+k1].v,询问O(1),空间复杂度O(n)

拓展:

  • 对于弹出队首,删除队尾元素以及压入元素条件的判定
    求凸包
  • 对于其他性质的运用
    单调栈

例题:

  • poj 2823
    给定序列长度n<106,窗口大小k,将窗口放到序列上,从左往右一格一格移动窗体,输出每个状态窗体中数字的最小值和最大值。
    解题报告
0 0
原创粉丝点击