(一维/二维)单调队列模板
Posted aemshana
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(一维/二维)单调队列模板相关的知识,希望对你有一定的参考价值。
一维单调队列
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;
}
以上是关于(一维/二维)单调队列模板的主要内容,如果未能解决你的问题,请参考以下文章