关于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;
}