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;
    }
    
  • 相关阅读:
    Eclipse部署项目到Tomcat中,class文件夹为空的解决方案
    微软(北京).NET俱乐部活动 (2010年6月26日) – Visual Studio 2010 /*LIFE RUNS ON CODE*/
    失望的Vista SP1
    急聘BI DW OLAP开发工程师 (北京)
    急聘.NET开发工程师 (北京)
    开篇
    Windows Vista User Account Control (UAC) 全新安全模块“用户帐户控制”
    Tidy your desktop
    [导入]Vista的屏幕截图小工具:Snipping Tool
    微软发布官方TFS 2010 Scrum 模板
  • 原文地址:https://www.cnblogs.com/AEMShana/p/12730217.html
Copyright © 2011-2022 走看看