转载请注明出处,谢谢http://blog.csdn.net/acm_cxlove/article/details/7854526 by---cxlove
题目:查询区间的最大值和最小值
http://poj.org/problem?id=3264
以前只会线段树的做法,nlgn的建树,lgn的查询。
因为RMQ作为一种题目常见的问题,有必须学习一下,可以作为一个工具出现。
ST算法是另外一种高效的求解区间最值的算法。nlgn的处理,可以达到o(1)的查询,而且线段树的常数也是非常大的。
它是一种动态规划的方法。
以最小值为例。a为所寻找的数组.
用一个二维数组f(i,j)记录区间[i,i+2^j-1](持续2^j个)区间中的最小值。其中f[i,0] = a[i];
所以,对于任意的一组(i,j),f(i,j) = min{f(i,j-1),f(i+2^(j-1),j-1)}来使用动态规划计算出来。
这个算法的高明之处不是在于这个动态规划的建立,而是它的查询:它的查询效率是O(1).
假设我们要求区间[m,n]中a的最小值,找到一个数k使得2^k<n-m+1.
这样,可以把这个区间分成两个部分:[m,m+2^k-1]和[n-2^k+1,n].我们发现,这两个区间是已经初始化好的.
前面的区间是f(m,k),后面的区间是f(n-2^k+1,k).
这样,只要看这两个区间的最小值,就可以知道整个区间的最小值!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 100005 using namespace std; int n,q,a[N]; int mx[N][18],mn[N][18]; void Rmq_Init(){ int m=floor(log((double)n)/log(2.0)); for(int i=1;i<=n;i++) mx[i][0]=mn[i][0]=a[i]; for(int i=1;i<=m;i++) for(int j=n;j;j--){ mx[j][i]=mx[j][i-1]; mn[j][i]=mn[j][i-1]; if(j+(1<<(i-1))<=n){ mx[j][i]=max(mx[j][i],mx[j+(1<<(i-1))][i-1]); mn[j][i]=min(mn[j][i],mn[j+(1<<(i-1))][i-1]); } } } int Rmq_Query(int l,int r){ int m=floor(log((double)(r-l+1))/log(2.0)); int Max=max(mx[l][m],mx[r-(1<<m)+1][m]); int Min=min(mn[l][m],mn[r-(1<<m)+1][m]); return Max-Min; } int main(){ while(scanf("%d%d",&n,&q)!=EOF){ for(int i=1;i<=n;i++) scanf("%d",&a[i]); Rmq_Init(); while(q--){ int l,r; scanf("%d%d",&l,&r); printf("%d\n",Rmq_Query(l,r)); } } return 0; }