ST算法的实现
ST算法是一种解决静态询问区间最值问题的高效离线算法,算法上采用了倍增的思想,而实现上则是利用了动态规划,具体流程如下所示:
对于区间[begin , end],将其拆分为两个区间:[begin , begin+]、[end- , end],,每个区间的长度为。然后继续拆分这两个区间,此时区间的长度为,此后每次拆分都将区间缩短一半(x-1),不断拆分直到区间长度为1(即x=0)为止。
设f[i][j]表示区间[i , i+]内的最值,则它拆分后的两个区间分别为:[i , i+]、[i+ , i+],即f[i][j-1]、f[i+][j-1]。由此,我们推出求区间最值的状态转移方程为:
F[ begin ][ x ]= max/min { F[ begin ][ x-1 ] , F[ begin+( 1<<x-1 ) ][ x-1 ] }。
而再加上第一次的拆分,就能得到区间[begin , end]的最值为:
Ans=max/min { F[ begin ][ x ] , F[ end-(1<<x)+1 ][ x ] }
我们已经得知每个区间x的值为区间长度取对数,可以直接调用cmath库中的对数函数,但是效率不高,而为了提升计算的效率,我们进行一个预处理,通过递推的方式求得1~length(区间长度)各自对应的x值:
一个简单推导: ,初值为
以上,就是st算法的全部内容了,下面来看一个简单例子,有如下一组数据,用st算法计算其最大值:
0 1 2 3 4 5 6 7 8 9 10 11
我们得到其区间长度为:12 ,首先预处理求出所有的x值:
1-0、 2-1、 3-1、 4-2、 5-2、 6-2、 7-2、 8-3、 9-3、 10-3、 11-3、 12-3
它能拆分出的最长的两个长度为的区间为:[0 , 7] [4 , 11],要求区间[0 , 11]的最大值,则只要求max{ [0 , 7] , [4 , 11] },所以最终答案即为: max{F[1][3] , f[12-(1<<3)+1][3]}.
然后继续对[0 , 7] [4 , 11]进行逐渐拆分,直到=1,求出每一个区间对应的极值:
每行依次返回最大值:
、
同理另一区间的返回值为11,所以得到最终的答案为max{7 , 11}=11.
示例代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100005, x = 25;
int num[N],Log[N];
int F[N][x]; //F的含义是从N到N+2^x-1(长度为2^x)的范围内的最大值/最小值
int main() {
int n, t;
scanf("%d%d",&n,&t);
for (int i = 1; i <= n; ++i)
scanf("%d",&num[i]);
Log[0] = -1;
for (int i = 1; i <= n; ++i)
F[i][0] = num[i], Log[i] = Log[i >> 1] + 1;
//递推顺序一定是先j后i
for (int j = 1; j <= x; ++j)
for (int i = 1; i + (1 << j) <= n + 1; ++i) //j实际上只用取到floor(log[n]);
F[i][j] = max(F[i][j - 1], F[i + (1 << j - 1)][j - 1]);//end=i+2^j-1 —> end<=n
while (t--) {
int a, b, ans = 0;
scanf("%d%d",&a,&b);
int X = Log[b - a + 1];
ans = max(F[a][X], F[b - (1 << X) + 1][X]);
printf("%d\n",ans);
}
return 0;
}