zoukankan      html  css  js  c++  java
  • (一维/二维)单调队列模板

    一维单调队列

    template<typename elemType,typename CMP>
    struct MPQueue{
        CMP cmp;
        int Pos[1000010];
        elemType Node[1000010];
        int head,tail,limit;//limit-滑动窗口长度
        MPQueue():head(1),tail(0),limit(1){}
        MPQueue(int _limit):head(1),tail(0),limit(_limit){}
        
        bool size(){return tail-head+1;}
        bool empty(){return tail<head;}//是否为空
        elemType front_elem(){return Node[head];}//获取队首的元素
        int front_pos(){return Pos[head];}//获取队首元素在原序列中的位置
        //以上操作使用前应保证先pop_front队首的过时元素
    
        void set_limit(int _limit){limit=_limit;}//设置滑动窗口长度
        void clear(){head=1,tail=0;}//清空单调队列
        void push(int pos,elemType elem){//插入元素,维护最小值
            //pos-元素在原序列中的位置,elem-要插入的元素,limit-滑动窗口的长度
            while(head<=tail && (!cmp(Node[tail],elem) || pos-Pos[tail]+1>limit)) 
                --tail;
            ++tail;Node[tail]=elem;Pos[tail]=pos;
        }
        void pop_front(int pos){//处理过时的队首元素
            while(head<=tail && pos-Pos[head]>=limit) ++head;
        }
        void Query(int *Ans,int *AnsPos,int *Data,int Len){//查询每一个滑动窗口的最值
            //Ans-滑动窗口的最值,AnsPos-滑动窗口最值在原序列里的位置
            //Data-原序列,Len-原序列长度
            for(RG i=1;i<=Len;++i){
                push(i,Data[i]);
                pop_front(i);
                if(Ans!=NULL) Ans[i]=front_elem();
                if(AnsPos!=NULL) AnsPos[i]=front_pos();
            }
            return;
        }
    };
    

    一维单调队列的使用 (洛谷 P1886)

    题目描述

    有一个长为 (n) 的序列 (a),以及一个大小为 (k) 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

    输入格式

    输入一共有两行,第一行有两个正整数 (n,k)。 第二行 (n) 个整数,表示序列 (a)

    输出格式

    输出共两行,第一行为每次窗口滑动的最小值。
    第二行为每次窗口滑动的最大值。

    输入样例

    8 3  
    1 3 -1 -3 5 3 6 7
    

    输出样例

    -1 -3 -3 -3 3 3
    3 3 5 5 6 7
    

    Code

    MPQueue<int,less<int> > Q1;//维护单调最小值的队列
    MPQueue<int,greater<int> > Q2;//维护单调最大值的队列
    int AnsMin[1000010],AnsMax[1000010];
    int Data[1000010];
    int N,K;
    
    int main(){
        Read(N);Read(K);
        Q1.set_limit(K);Q2.set_limit(K);//设置滑动窗口长度
        for(RG i=1;i<=N;++i)
            Read(Data[i]);
        Q1.Query(AnsMin,NULL,Data,N);//我们不需要知道最值的位置,所以AnsPos设为NULL
        Q2.Query(AnsMax,NULL,Data,N);
        for(RG i=K;i<=N;++i){
            printf("%d",AnsMin[i]);
            if(i<N) printf(" ");
        }
        printf("
    ");
        for(RG i=K;i<=N;++i){
            printf("%d",AnsMax[i]);
            if(i<N) printf(" ");
        }
        printf("
    ");
        return 0;
    }
    

    二维单调队列

    template<typename elemType,typename CMP>
    struct MPQueue{
        CMP cmp;
        int Pos[1010];
        elemType Node[1010];
        int head,tail,limit;//limit-滑动窗口长度
        MPQueue():head(1),tail(0),limit(1){}
        MPQueue(int _limit):head(1),tail(0),limit(_limit){}
        
        bool size(){return tail-head+1;}
        bool empty(){return tail<head;}//是否为空
        elemType front_elem(){return Node[head];}//获取队首的元素
        int front_pos(){return Pos[head];}//获取队首元素在原序列中的位置
        //以上操作使用前应保证先pop_front队首的过时元素
    
        void set_limit(int _limit){limit=_limit;}//设置滑动窗口长度
        void clear(){head=1,tail=0;}//清空单调队列
        void push(int pos,elemType elem){//插入元素,维护最小值
            //pos-元素在原序列中的位置,elem-要插入的元素,limit-滑动窗口的长度
            while(head<=tail && (!cmp(Node[tail],elem) || pos-Pos[tail]+1>limit)
                --tail;
            ++tail;Node[tail]=elem;Pos[tail]=pos;
        }
        void pop_front(int pos){//处理过时的队首元素
            while(head<=tail && pos-Pos[head]>=limit) ++head;
        }
    };
    
    template<typename elemType,typename CMP>
    struct MPQueue2D{//二维单调队列
        struct NODE{int posy;elemType Value;};
        struct CMP2{bool operator()(const NODE &A,const NODE &B)const {
            return CMP()(A.Value,B.Value);}
        };
        MPQueue<elemType,CMP> Row[1010];
        MPQueue<NODE,CMP2> Col;
        int x[1010][1010],y[1010][1010];//x[i][j]-以(i,j)为右下角的子矩阵中最值的行号
                                        //y[i][j]-以(i,j)为右下角的子矩阵中最值的列号
        elemType val[1010][1010],Data[1010][1010];
        //val[i][j]-以(i,j)为右下角的子矩阵中的最值
        //Data-二维单调队列的数据源
        int N,M,limitR,limitC;
        //N-行数,M-列数,limitR-滑动的子矩阵的行数,limitC-滑动的子矩阵的列数
        
        MPQueue2D(int _N=0,int _M=0,int _limitR=0,int _limitC=0):
            N(_N),M(_M),limitR(_limitR),limitC(_limitC) {}
        void setN(int _N){N=_N;}
        void setM(int _M){M=_M;}
        void clear(){
            for(RG i=1;i<=N;++i)
                Row[i].clear();
            Col.clear();
        }
        void set_limit(int _limitR,int _limitC){//设置滑动子矩阵的行数limitR和列数limitC
            limitR=_limitR;limitC=_limitC;
            for(RG i=1;i<=N;++i)
                Row[i].set_limit(limitC);
            Col.set_limit(limitR);
        }
        void Query(){//查询二维数组中每个滑动子矩阵的最值及其位置
            for(RG j=1;j<=M;++j){
                Col.clear();//维护列的单调队列
                for(RG i=1;i<=N;++i){
                    Row[i].push(j,Data[i][j]);
                    Row[i].pop_front(j);
                    Col.push(i,(NODE){Row[i].front_pos(),Row[i].front_elem()});
                    Col.pop_front(i);
                    x[i][j]=Col.front_pos();//以(i,j)为右下角的子矩阵中最值的行号
                    y[i][j]=Col.front_elem().posy;//以(i,j)为右下角的子矩阵中最值的列号
                    val[i][j]=Col.front_elem().Value;//以(i,j)为右下角的子矩阵中的最值
                }
            }
        }
    };
    

    二维单调队列的使用 (洛谷 P2216,[HAOI2007]理想的正方形)

    题目描述

    有一个 (N imes M) 的整数组成的矩阵,现请你从中找出一个 (K imes K) 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

    输入格式

    第一行为3个整数,分别表示 (N,M,K) 的值

    第二行至第 (N+1) 行每行为 (M) 个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

    输出格式

    仅一个整数,为 (N imes M) 矩阵中所有“ (K imes K) 正方形区域中的最大整数和最小整数的差值”的最小值。

    样例输入

    5 4 2
    1 2 5 6
    0 17 16 0
    16 17 2 1
    2 10 2 1
    1 2 2 2
    

    样例输出

    1
    

    Code

    MPQueue2D<int,less<int> > Q1;//维护滑动子矩阵单调最小值的二维单调队列
    MPQueue2D<int,greater<int> > Q2;//维护滑动子矩阵最大值的二维单调队列
    int N,M,K;
    
    int main(){
        Read(N);Read(M);Read(K);
        Q1.N=Q2.N=N;Q1.M=Q2.M=M;
        Q1.set_limit(K,K);Q2.set_limit(K,K);//设置滑动子矩阵的行列数
        for(RG i=1;i<=N;++i)
            for(RG j=1;j<=M;++j){
                Read(Q1.Data[i][j]);//读入矩阵
                Q2.Data[i][j]=Q1.Data[i][j];
            }
        Q1.Query();Q2.Query();
        int Ans=INF;
        for(RG i=K;i<=N;++i)
            for(RG j=K;j<=M;++j)//因为子矩阵要完整,所以i,j都从K开始
                Ans=min(Ans,Q2.val[i][j]-Q1.val[i][j]);
        printf("%d
    ",Ans);
        return 0;
    }
    
  • 相关阅读:
    20080619 SQL SERVER 输入 NULL 的快捷键
    20090406 Adobe的“此产品的许可已停止工作”错误的解决办法
    20080908 Office Powerpoint 2007 不能输入中文的解决办法
    20080831 ClearGertrude Blog Skin 's cnblogs_code class
    20080603 Facebook 平台正式开放
    20080519 安装 Microsoft SQL Server 2000 时提示 创建挂起的文件操作
    test
    Linux—fork函数学习笔记
    SOA的设计理念
    Why BCP connects to SQL Server instance which start with account of Network Service fail?
  • 原文地址:https://www.cnblogs.com/AEMShana/p/12730217.html
Copyright © 2011-2022 走看看