• luogu-单调队列/单调栈专题


    下面所有单调队列和单调栈,我使用的 双端队列 STL 的 deque 模拟操作

    P2698 [USACO12MAR]花盆Flowerpot

    题意:

    给出水滴的坐标与下落时间,你需要构造一个盆,使他的宽度满足
    在其范围内能够接住水滴时间(第一滴和最后一滴/最大与最小值)时间差大于等于k,且使得这个盆的直径最小

    思路:

    会想到尺取法(双指针)来在符合条件内缩小范围,同时又涉及到区间最值问题,那么上来想就很容易想到单调队列或者单调栈来维护区间最值
    两点:满足区间最小与最大之差大于k,且使得其长度最小。
    (1)区间最值:由于是要进行双指针操作:所以选择双端队列来实现单调队列维护最值,且能维护在 q.front().x ~ q.back().x的区间最值

    (2)在符合条件下对 front/队首进行移动,以达到缩小区间操作

    code:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5+7;
    const int inf = 0x3f3f3f3f;
    struct node{
        int x,y;
    };
    bool cmp(node a,node b){
        return a.x<b.x;
    }
    node arr[maxn];
    deque<node>q1;
    deque<node>q2;
    int main(){
        int n,k;
        cin>>n>>k;
        for(int i=0;i<n;i++){
            cin>>arr[i].x>>arr[i].y;
        }
        sort(arr,arr+n,cmp);
        int ans = inf;
        for(int i=0;i<n;i++){
            node tmp;
            //维护最大最小单调队列
            while(!q1.empty()&&q1.back().y<arr[i].y) q1.pop_back();
            tmp = arr[i];
            q1.push_back(tmp);
            while(!q2.empty()&&q2.back().y>arr[i].y) q2.pop_back();
            q2.push_back(tmp);
            while(!q1.empty()&&!q2.empty()&&q1.front().y-q2.front().y>=k){
                node pos;
                if(q1.front().x<q2.front().x) {pos = q1.front(); q1.pop_front();}
                else {pos = q2.front(); q2.pop_front();}
                ans = min(ans,arr[i].x-pos.x);
            }
        }
        if(ans!=inf) cout<<ans<<endl;
        else cout<<-1<<endl;
    }
    View Code

     P2564 [SCOI2009]生日礼物

    题意:给出n个珠子,m种颜色( 用1~m的序号表示 ),然后给出每种颜色的珠子的个数以及位置,选择出最小的区间 涵盖所有颜色种类的珠子。

    思路:(这道题感觉和单调队列/栈没啥关系)尺取法,如果被新放入的位置使得涵盖了所有的颜色,就对尾部进行收缩,减小其长度。

    code:

    //把所有的颜色全部包含的最小长度
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e6+5;
    const int inf = 0x3f3f3f3f;
    int cont[maxn];
    struct node{
        int pos,color;
    }nodes[maxn];
    bool cmp(node a,node b){
        return a.pos<b.pos;
    }
    deque<node>q1;//滑动窗口
    int main(){
        int n,m;
        cin>>n>>m;
        int cnt = 0;
        memset(cont,0,sizeof(cont));
        for(int i=1;i<=m;i++){
            int t;
            cin>>t;
            while(t--){
                int tmp;
                cin>>tmp;
                nodes[cnt++] = (node){tmp,i};
            }
        }
        sort(nodes,nodes+cnt,cmp);
        int tot = 0;
        int ans = inf;
        for(int i =0;i<cnt;i++){
            //操作:如果所有颜色全部被包含就尝试缩短长度
            //如何有效判断颜色被全部包含?设置tot,用于统计当前颜色总数
            //如果颜色在序列中大于1则pop出去
            q1.push_back(nodes[i]);
            cont[nodes[i].color]++;
            //如果是新增加的颜色提供的贡献
            if(cont[nodes[i].color]==1) tot++;
            while(!q1.empty()&&cont[q1.front().color]>1){
                cont[q1.front().color]--;
                if(cont[q1.front().color]<1) tot--;
                q1.pop_front();
            }
            if(tot==m) ans = min(ans,(q1.back().pos-q1.front().pos));
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

     

    P2216 [HAOI2007]理想的正方形

    题意:给出a,b,n . 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

    思路:数据比较小 0<=a,b<=1000, 且每个值 小于 1e9 ( int 范围内 ),所以暴力一点的思路就是使用单调队列 :先横着扫一边,使用类似滑动窗口来限制区间大小,得到每行中的最值。然后在竖着扫一边,求出所有正方形的 最值。最后 对每一个正方形遍历一边得到最值。

    code:

    //题意:给出一个a*b的矩阵,找出n*n的正方形区域,使得区域中素有数的最大值和最小值最小
    //思路:维护所有n*n矩阵的差值,区间就利用滑动窗口来维护最值
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e3+7;
    const int inf = 0x3f3f3f3f;
    int mt[maxn][maxn];
    int maxrow[maxn][maxn];
    int minrow[maxn][maxn];
    int maxcol[maxn][maxn];
    int mincol[maxn][maxn];
    struct node{
        int val,pos;
    };
    deque<node>q1;
    deque<node>q2;
    int main(){
        int n,m,N;
        cin>>n>>m>>N;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                cin>>mt[i][j];
            }
        }
        //得到每个符合条件区间的最大值
        //先得到 每行滑动窗口内的最值,
        for(int i=0;i<n;i++){
            while(!q1.empty()) q1.pop_back();
            while(!q2.empty()) q2.pop_back();
            for(int j=0;j<m;j++){
                //保证单增
                while(!q1.empty()&&q1.back().val<mt[i][j])
                q1.pop_back();
                q1.push_back((node){mt[i][j],j});
                //维护滑动窗口长度
                while(!q1.empty()&&(q1.back().pos-q1.front().pos>N-1)) q1.pop_front();
                if(j>=N-1) maxrow[i][j] = q1.front().val;
                //保证单减
                while(!q2.empty()&&q2.back().val>mt[i][j])
                q2.pop_back();
                q2.push_back((node){mt[i][j],j});
                while(!q2.empty()&&(q2.back().pos-q2.front().pos>N-1)) q2.pop_front();
                if(j>=N-1) minrow[i][j] = q2.front().val;
            }
        }
        int ans = inf;
        for(int i=N-1;i<m;i++){
            while(!q1.empty()) q1.pop_back();
            while(!q2.empty()) q2.pop_back();
            for(int j=0;j<n;j++){
                while(!q1.empty()&&q1.back().val<maxrow[j][i])
                q1.pop_back();
                q1.push_back((node){maxrow[j][i],j});
                while(!q1.empty()&&(q1.back().pos-q1.front().pos>N-1)) q1.pop_front();
                if(j>=N-1) maxcol[j][i] = q1.front().val;
    
                while(!q2.empty()&&q2.back().val>minrow[j][i])
                q2.pop_back();
                q2.push_back((node){minrow[j][i],j});
                while(!q2.empty()&&(q2.back().pos-q2.front().pos>N-1)) q2.pop_front();
                if(j>=N-1) mincol[j][i] = q2.front().val;
            }
        }
        for(int i=N-1;i<n;i++){
            for(int j=N-1;j<m;j++){
                ans = min(ans,maxcol[i][j]-mincol[i][j]);
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code


     

  • 相关阅读:
    Shodan在渗透测试及漏洞挖掘中的一些用法
    QUdpSocket 简单用法
    用QT操作数据库(本周学的)
    Qt使用UDp通信、套接字socket的成员函数bind()的作用
    ppm的含义
    数字的补数
    两数之和
    C++中的最大整数最小整数
    如何使用dockerfile将jar包生成镜像
    python3解决 json.dumps中文乱码
  • 原文地址:https://www.cnblogs.com/Tianwell/p/11920435.html
Copyright © 2020-2023  润新知