zoukankan      html  css  js  c++  java
  • 单调队列总结

    定义

    单调队列,即单调递减或单调递增的队列。

    入门题(洛谷P1886滑动窗口)

    传送门

    题目描述

    分析

    如果用暴力求解的话,我们要将这一个长度为(k)的区间扫一遍
    但是实际上,有很多值是显然不会对答案产生贡献的
    比如我们要维护该区间的最大值,当前队尾的的元素是(4),下一个要加进去的元素是(5)
    此时队尾一定不会对答案产生贡献,因为它的值比下一个元素小,而且当前值继续对答案产生贡献的时间也更短
    这样,我们就相当于维护了一个单调递减的队列
    维护区间最小值同理

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+5;
    typedef long long ll;
    ll a[maxn];
    int q[maxn];
    int main(){
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        int head=1,tail=0;
        for(int i=1;i<=n;i++){
            while(head<=tail && i-q[head]+1>k) head++;
            while(head<=tail && a[i]<a[q[tail]]) tail--;
            q[++tail]=i;
            if(i>=k)printf("%lld ",a[q[head]]);
        }
        printf("
    ");
        head=1,tail=0;
        for(int i=1;i<=n;i++){
            while(head<=tail && i-q[head]+1>k) head++;
            while(head<=tail && a[i]>a[q[tail]]) tail--;
            q[++tail]=i;
            if(i>=k)printf("%lld ",a[q[head]]);
        }
        printf("
    ");
        return 0;
    }
    

    其它题目

    P2952 [USACO09OPEN]Cow Line S
    P1440 求m区间内的最小值
    P1638 逛画展
    P1901 发射站
    P2032 扫描
    P2947 [USACO09MAR]Look Up S
    P1714 切蛋糕
    P2629 好消息,坏消息

    单调队列优化DP

    P2627 [USACO11OPEN]Mowing the Lawn G

    题目描述

    分析

    暴力的(DP)方程比较好想,我们设(f[i])为选择到第(i)头奶牛,并且第(i)头奶牛必须选的最大价值
    (f[i]=max(f[i],f[j]+sum[i]-sum[j+1])(i-(j+1)<=k))
    我们只需要用单调队列搞一下(f[j]-sum[j+1])的最大值即可

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+5;
    typedef long long ll;
    ll f[maxn],head,tail,sum[maxn],a[maxn];
    int q[maxn];
    int main(){
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=n;i++){
            sum[i]=sum[i-1]+a[i];
            if(i<=k) f[i]=sum[i];
        }
        ll ans=0;
        head=1,tail=1;
        for(int i=1;i<=n;i++){
            while(head<=tail && i-q[head]-1>k) head++;
            f[i]=max(f[i],f[q[head]]+sum[i]-sum[q[head]+1]);
            ans=max(ans,f[i]);
            while(head<=tail && f[i]-sum[i+1]>f[q[tail]]-sum[q[tail]+1]) tail--;
            q[++tail]=i;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    P1725 琪露诺

    题目描述

    分析

    对于每一个给定的(i),我们都可以从([i-r,i-l])区间中选出一个最大的(f)值来更新
    用一个单调递减的队列维护即可
    注意队列中存的是位置,而不是标号

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+5;
    int a[maxn],q[maxn],f[maxn];
    int main(){
        for(int i=0;i<maxn;i++){
            f[i]=-0x3f3f3f3f;
        }
        int n,l,r;
        scanf("%d%d%d",&n,&l,&r);
        for(int i=0;i<=n;i++){
            scanf("%d",&a[i]);
        }
        f[0]=0;
        int head=1,tail=0,cnt=0;
        for(int i=l;i<=n;i++){
            while(head<=tail && f[q[tail]]<=f[i-l]) tail--;
            q[++tail]=i-l;
            while(q[head]+r<i) head++;
            f[i]=f[q[head]]+a[i];
        }
        int ans=-0x3f3f3f3f;
        for(int i=n-r+1;i<=n;i++){
            ans=max(ans,f[i]);
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    P3957 跳房子

    题目描述

    分析

    这道题刚一看上去和上一道题一模一样,但是队列里面存储的不是位置而是标号
    因此我们要用双重循环来维护

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5e5+5;
    int n,d,k;
    int f[maxn],q[maxn],a[maxn],wz[maxn];
    bool jud(int dd){
        for(int i=0;i<maxn;i++) f[i]=-0x3f3f3f3f;
        memset(q,0,sizeof(q));
        int tmin=max(1,d-dd),tmax=d+dd;
        int ans=0;
        int head=1,tail=0;
        f[0]=0;
        for(int i=1,j=0;i<=n;i++){
            while(wz[i]-wz[j]>=tmin && j<i){
                if(f[j]!=-0x3f3f3f3f){
                    while(head<=tail && f[q[tail]]<=f[j]) tail--;
                    q[++tail]=j;
                }
                j++;
            }
            while(head<=tail && wz[i]-wz[q[head]]>tmax) head++;
            if(head<=tail) f[i]=f[q[head]]+a[i];
            ans=max(ans,f[i]);
        }
        if(ans>=k) return 1;
        return 0;
    }
    int main(){
        scanf("%d%d%d",&n,&d,&k);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&wz[i],&a[i]);
        }
        int l=0,r=1e9,mids;
        while(l<=r){
            mids=(l+r)>>1;
            if(jud(mids)) r=mids-1;
            else l=mids+1;
        }
        if(l>1e9) printf("-1
    ");
        else printf("%d
    ",l);
        return 0;
    }
    

    其它题目

    P2422 良好的感觉
    P3572 [POI2014]PTA-Little Bird
    P3800 Power收集
    P3594 [POI2015]WIL-Wilcze doły

  • 相关阅读:
    《剑指offer》 面试题43 n个骰子的点数 (java)
    《剑指offer》面试题45 圆圈中最后剩下的数字(Java版本)
    《剑指offer》面试题39 二叉树的深度(java)
    《剑指offer》面试题32----从1到n整数中1出现的次数
    快速排序思路整理
    《Java程序猿面试宝典》之字符串
    Tomcat的server.xml
    easyui combobox 清除选中项目 和 清空option选项
    2019
    throw UnsupportedOperationException
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13406472.html
Copyright © 2011-2022 走看看