关于ST算法,实际上它本身并不难,它的思想是动态规划。主要用来求RMQ问题,时间复杂度为O(NlgN+M) 


关于RMQ问题描述:

输入N个数和M次询问,每次询问一个区间[L,R],求第L个数到R个数之间的最大值,或者是求最小值。


它的原理阐述如下:

对于一个数组A[0...N-1],我们用f[i][j]表示A[i]到A[i+2^j-1],这个范围内的最大值。

由于此区间的元素个数很明显为2^j个,所以我们又可以从中间平分为两部分,这样每部分又有2^(j-1)个元素,这样我们就知道区间[i,i+2^j-1]可以分为[i,i+2^(j-1)-1]和[i+2^(j-1),i+2^j-1]两部分,我们只需要求出后面两个区间最大值的较大值,就可以知道前面区间的最大值了。


所以到了这里,很明显可以写出状态转移方程:


f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])


当然很明显知道初始化f[i][0]=A[i]


当然上面i,j的范围是多少呢?


现在我们来分析一下:我们已经说了如果用上述原理一个区间的元素是2^j个,而可以知道2^j<=N的,所以这样就得到j<=log(N)/log(2);  当然j还大于等于1


对于i,就直接有i+2^j-1<N就行了。



到了这里,我们就可以把f[i][j]求出来了。


接下来就是query()了。


这个怎么办呢,其实很容易,我们先求出满足条件2^x=R-L+1的最大x


这样我们我们就可以把区间[L,R]求最值问题转化为了求区间[L,L+2^x-1]和区间[R-2^x+1,R]最大值的较大值了,为什么可以这样做,因为这两个区间中间有重叠。


但是这两个区间的并一定等于区间[L,R],所以到了这里ST算法的原理基本常阐述完毕了。


剩下的就是代码实现了。

#include <stdio.h>
#include <math.h>
#define N 1005

int m,n;
int a[N];
int f[N][N];

int max(int a,int b)
{
    return a>b? a:b;
}

void ST()
{
    int i,j;
    for(i=0;i<n;i++)
       f[i][0]=a[i];
    for(j=1;j<=(int)((log((double)n)/log(2.0)));j++)
    {
        for(i=0;i+(1<<j)-1<n;i++)
           f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    }
}

int query(int L,int R)
{
    int x=(int)(log((double)(R-L+1))/log(2.0));
    return max(f[L][x],f[R-(1<<x)+1][x]);
}

int main()
{
    int i,L,R;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=0;i<n;i++)
           scanf("%d",&a[i]);
        ST();
        while(m--)
        {
            scanf("%d%d",&L,&R);
            printf("%d\n",query(L-1,R-1));
        }
    }
    return 0;
}