zoukankan      html  css  js  c++  java
  • 单调队列 学习笔记

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

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

    例题 最大子序和

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

    $N,Mleq 3*10^5$。

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

    固定右端点$i$,此时要找到一个左端点$j$,$i-mleq jleq 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-kleq j<i)$。

    可以转化成$f[i][1]=max(f[j][0]-sum[j])+sum[i] (i-kleq 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;
    }
  • 相关阅读:
    Python--学习过程
    线程、进程、协程
    socket
    面向对象--进阶
    面向对象--初级
    Python常用的模块
    2019-2020-1 20191301《信息安全专业导论》第十二周学习总结
    wirehark
    2019--2020第十一周信息安全导论论总结20191301
    2019--2020信息安全导论第10周总结20191301
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/12824360.html
Copyright © 2011-2022 走看看