单调队列 学习笔记

单调队列是一种特殊的双端队列,其满足单调性,即内部元素单调递增或单调递减。单调队列可以用数组模拟,也可以用$STL$中的$deque$实现。

---------------------------

例题 最大子序和

给定一个长度为$N$的整数序列,从中找出一段长度不超过$M$的连续子序列,使得子序列中所有数的和最大。

$N,M\leq 3*10^5$。

区间和可以转化成“两个前缀和相减”求解。所以问题转化为“找出两个位置$x,y$,使得$S[y]-S[x]$最大且$y-x\leq M$。”

固定右端点$i$,此时要找到一个左端点$j$,$i-m\leq j\leq i-1$且$S[j]$最小。

比较一下任意两个位置$j$和$k$。如果$k<j<i$并且$S[k]\geq S[j]$,那么对于大于等于$i$的右端点,$k$永远不会成为最优选择。因为$S[k]\geq S[j]$且$j$更靠近$i$,,即$j$的生存能力更强,长度更不容易超过$M$。

以上告诉我们,最优选择的策略集合一定是一个下标递增,对应前缀和$S$也递增的一个序列。

利用单调队列维护,有3个步骤:

1.判断队头决策与$i$的距离是否超过$M$,若是则出队。

2.此时队头就是最优选择。

3.不断删除队尾决策直到队尾值小于$S[i]$。然后将$i$作为新的决策入队。

代码:

int l=1,r=1;
q[1]=0;
for (int i=1;i<=n;i++)
{
    while(l<=r&&q[l]<i-m) l++;
    ans=max(ans,sum[i]-sum[q[l]]);
    while(l<=r&&sum[q[r]]>=sum[i]) r--;
    q[++r]=i;
}

单调队列优化线性DP

形如$f[i]=max{f[j]}+value$的DP方程,我们可以尝试用单调队列优化。

例题 [USACO11OPEN]Mowing the Lawn G

题目链接

----------------------------

状态:设$f[i][0]$表示以$i$结尾且$i$这个数不选所得的最大效率值;$f[i]i1]$表示选这个数的最大效率值。

不难得出状态转移方程:

$f[i][0]=max(f[i-1][0],f[i-1][1])$。

$f[i][1]=max(f[j][0]+sum[i]-sum[j]) (i-k\leq j<i)$。

可以转化成$f[i][1]=max(f[j][0]-sum[j])+sum[i] (i-k\leq j<i)$。可以用单调队列优化。

注意开$long long$。

代码:

#include<bits/stdc++.h>
#define int  long long
using namespace std;
int f[100005][2],n,k;
int q[100005],l=1,r=1;
int sum[100005],a[100005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
signed main()
{
    n=read(),k=read();
    for (int i=1;i<=n;i++)
    {
        a[i]=read();
        sum[i]=sum[i-1]+a[i];
    }
    int l=1,r=1;
    for (int i=1;i<=n;i++)
    {
        f[i][0]=max(f[i-1][0],f[i-1][1]);
        while(q[l]<i-k&&l<=r) l++;
        f[i][1]=f[q[l]][0]-sum[q[l]]+sum[i];
        while(f[i][0]-sum[i]>f[q[r]][0]-sum[q[r]]&&l<=r) r--;
        q[++r]=i;
    }
    printf("%ld",max(f[n][0],f[n][1]));
    return 0;
}

 

posted @ 2020-05-03 22:43  我亦如此向往  阅读(167)  评论(0编辑  收藏  举报