RMQ小结 poj 3264 poj3368
2017-03-16 19:58
148 查看
1. 概述
RMQ(Range Minimum/MaximumQuery),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。这两个问题是在实际应用中经常遇到的问题。每次用一个循环来计算区间最值显然不够快,怎么办呢?实践中最常用的是Tarjan的Sparse-Table 算法,预处理O(nlgn),但是查询时间只要O(1),而且常数很小。更重要是,这个算法比线段树好写多了(除了ZKW大牛的线段树 Orz我看过最简洁的写法).
2.原理
令d(i,j)表示 从i开始的,长度为xj的一段元素中的最值,则可以用递推的方式计算d(i,j)=d(i,j)=min{d(i,j),d(i+2j−1,j-1)}。
注意2j<=n,因此d数组的元素个数不超过nlogn,每一项又可以在常数时间计算出来,固总时间是O(nlogn)。
预处理模板:
void RMQ_init(const vector<int>& A){//下标从0开始 int n=A.size(); for(int i=0;i<n;i++) dmin[i][0]=A[i],dmax[i][0]=A[i]; for(int j=1;(1<<j)<=n;j++){ for(int i=0;i+(1<<j)-1<n;i++){ dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]); dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]); } } }
查询
查询操作很简单,另k为满足2k<=R-K+1的最大整数,则L开头,以R结尾的两个长度为2k的区间合起来覆盖了区间[L,R].由于取得是极值,有些元素重复考虑也没有关系。(注意如果是累加,重复元素是不允许的)。int rmq(int l,int r,int ok){//ok=0返回最小值,ok=1返回最大值 int k=0; while((1<<(k+1))<=r-l+1) k++; return ok==0 ? min(dmin[l][k],dmin[r-(1<<k)+1][k]) : max(dmax[l][k],dmax[r-(1<<k)+1][k]); }
题目:
poj 3264
裸的求区间极值问题,直接套模板。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; const int N=(1<<16); int n,q,dmin [20],dmax [20],a ; void RMQ_init(){ for(int i=0;i<n;i++) dmin[i][0]=a[i],dmax[i][0]=a[i]; for(int j=1;(1<<j)<=n;j++){ for(int i=0;i+(1<<j)-1<n;i++){ dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]); dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]); } } } int rmq(int l,int r,int ok){ int k=0; while((1<<(k+1))<=r-l+1) k++; return ok==0 ? min(dmin[l][k],dmin[r-(1<<k)+1][k]) : max(dmax[l][k],dmax[r-(1<<k)+1][k]); } int main(){ while(~scanf("%d %d",&n,&q)){ for(int i=0;i<n;i++) scanf("%d",&a[i]); //for(int i=0;i<n;i++) cout<<a[i]<<endl; RMQ_init(); for(int i=0;i<q;i++){ int l,r;scanf("%d%d",&l,&r); printf("%d\n",rmq(l-1,r-1,1)-rmq(l-1,r-1,0)); } } }
poj3368&&uva11235
题意:给你一组非降序元素,查询某个区间内连续出现次数最多的一个数出现的次数.
思路:用value[i]和cnt[i]分别表示第i段的数值和出现的次数,l[i],r[i]表示第i段的数值最左边和最右边端点的位置,num[p]表示位置p所在段的编号。
每次查询[L,r]区间的结果为以下3个部分的最大值:从L到L所在段的结束处的元素的个数(r[L]-L+1),从R到R所在段结束处元素的个数(R-l[R]+1),中间num[L]+1段到num[R]-1段的cnt的最大值,而这部分求解就用到RMQ。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1<<17; int value ,cnt ,num ,l ,r ,d [20],t,a ; void RMQ_init(){ memset(d,0,sizeof(d)); for(int i=0;i<t;i++) d[i][0]=cnt[i]; for(int j=1;(1<<j)<=t;j++){ for(int i=0;i+(1<<j)-1<t;i++){ d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]); } } } int rmq(int l,int r){ int k=0; while((1<<(k+1))<=r-l+1) k++; return max(d[l][k],d[r-(1<<k)+1][k]); } int main(){ int n,q; while(scanf("%d",&n)&&n){ scanf("%d",&q); int lx;scanf("%d",&lx);value[0]=lx;cnt[0]=1;num[0]=t;l[0]=0;r[0]=0; for(int i=1;i<n;i++){ int x;scanf("%d",&x); if(lx!=x){ t++; value[t]=x;cnt[t]=0; num[i]=t;l[t]=i;r[t]=i; } else{ num[i]=t;r[t]=i; cnt[t]=max(cnt[t],i-l[num[i]]+1); } lx=x; } t++; RMQ_init(); for(int i=0;i<q;i++){ int L,R;scanf("%d%d",&L,&R);L--;R--; if(num[L]==num[R]) printf("%d\n",R-L+1); else{ int ans=max(r[num[L]]-L+1,R-l[num[R]]+1); if(num[L]+1<=num[R]-1) ans=max(ans,rmq(num[L]+1,num[R]-1)); printf("%d\n",ans); } } } }
相关文章推荐
- RMQ的ST算法学习小记 Poj 3264 Balanced Lineup
- POJ3264 Balanced Lineup (RMQ & ST)
- POJ 3264 Balanced Lineup RMQ问题 ST算法
- POJ 3264-Balanced Lineup-RMQ问题
- POJ 3264 Balanced Lineup(RMQ模版)
- POJ 3264 RMQ--ST 算法
- RMQ 问题 POJ 3264 求解任意指定区间内的最小值和最大值
- POJ 3264 Balanced Lineup (RMQ)
- POJ 3264 RMQ模版
- POJ 3264 Balanced Lineup (RMQ)
- POJ3264 【RMQ基础题—ST-线段树】
- 【POJ 3264】Balanced Lineup(RMQ算法||线段树)
- poj 3264 Balanced Lineup rmq
- POJ 3264 Balanced Lineup (线段树||RMQ)
- POJ-3264 RMQ
- poj 3264 Balanced Lineup(RMQ求区间最值)
- poj 3264 Balanced Lineup(RMQ)
- POJ 3264 ST表(RMQ问题:查询区间最大最小值)
- POJ-3264 RMQ 线段树与ST
- 【bzoj 1699 & 1636】【POJ 3264】Balanced Lineup(st表|RMQ)