静态RMQ的Sparse Table算法

来源:互联网 发布:安卓 Linux 编辑:程序博客网 时间:2024/06/05 17:25

     本文算法的核心内容以及关键源代码均来自http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestorRMQ部分,但是加入了一些说明和插图。严格的说,本文是一篇意译文章。

    RMQ,Range Minimum/Maximum Query,区间极值问题,是指给定源序列A的情况下,询问区间[i,j]之间所有数的极值。如果只问1次,这就不能称之为一个问题。现在的问题是有多次查询,每一次都会询问不同区间的极值,如何有效的回答。

    带预处理的暴力法可以这样做,计算辅助矩阵M[n][n],n为A的长度,令M[i][j]即A[i,j]的区间极值。则做一个双重循环即可求出M。对于每次查询,直接给出M[i][j]即可。

    Sparse Table算法则将矩阵设置为M[n][logn+1],每一个M[i][j]均代表A[i, i+2^j)的区间极值。首先,如何求出M中的每一个数?使用倍增思想。


    如上图,当j大于0时,任何一个M[i][j]必然是M[i][j-1]和M[x][j-1]二者的极值,x显然是i+2^(j-1)。而最初始,M[i][0]就等于A[i]。

   剩下的问题就是如何利用M回答A[i,j]的区间极值。


     令k = log(j-i+1),则区间如上示意,只需解决y是多少,A[i, j]的区间极值就是M[i][k]和M[y][k]二值的极值。总体而言,ST是一个非常简单的方法,也只能解决A元素不变的RMQ。如果在查询操作之间,A的元素还能改变,则不适用。

  POJ3264,求区间极值之差。

#include <stdio.h>#include <math.h>int A[50000];int Max[50000][15];int Min[50000][15];void init(int n){int i,j;//初始化,长度为1的区间for(i=0;i<n;i++) Max[i][0] = Min[i][0] = *(A+i);//类似倍增法for(j=1;(1<<j)<=n;j++){for(i=0;i+(1<<j)<=n;i++){//第一段的极值    int x = Max[i][j-1];//第二段的极值int y = Max[i+(1<<(j-1))][j-1];if ( x >= y ) Max[i][j] = x;else          Max[i][j] = y;x = Min[i][j-1];y = Min[i+(1<<(j-1))][j-1];if ( x <= y ) Min[i][j] = x;else          Min[i][j] = y;}}return ;}int query(int a,int b){int k;int x,y;int maxak,minak,maxkb,minkb;if ( a == b ) return 0;//求区间长度的对数k = (int)( log((double)(b-a+1)) / log(2.0) );//求以a为首的k长度的极值    maxak = Max[a][k];minak = Min[a][k];//求以b结尾的k长度极值    maxkb = Max[b+1-(1<<k)][k];minkb = Min[b+1-(1<<k)][k];if ( maxak < maxkb ) maxak = maxkb;if ( minak > minkb ) minak = minkb;return maxak - minak;}int main(){int n,q;int i;int a,b;scanf("%d%d",&n,&q);for(i=0;i<n;i++) scanf("%d",A+i);init(n);for(i=0;i<q;i++){scanf("%d%d",&a,&b);printf("%d\n",query(a-1,b-1));}return 0;}


1 0