前面先给大家讲一下ST表的基础原理
ST表是一种常用算法,用于快速求区间的最值,当要大量计算区间最值时,使用ST表可以节省时间。
其复杂度为NlogN, 普通算法为N*N,缺点是不能在线修改值,(还有一种算法叫线段树可以,以后会讲)
ST[ i ][ j ] 的值为区间[ i, i + 2^j) 的最值,注意区间为左闭右开;
利用动态规划思想,区间[ i, i + 2^j) 的最值为 区间[ i, i + 2^(j - 1) ) 和
区间[ i + 2^(j - 1) ,i + 2^(j - 1)+ 2^(j - 1) ) ( 即 区间[ i + 2^(j - 1) ,i + 2^j ) 的最值,
因此推出 状态转移方程 ST[i][j+1] = max( ST[i][j] , ST[i + ( 1 << j)][j]); 1 << j 等于 2^j
下面即我写的代码,为了构造Find 函数, 我加入了一个数组mn[ ], log n, 但是当其值不为整数时向下取整。
写这个数组时我用了一些比较有意思的代码,大家可以仔细分析一下。
其实ST表中最重要的是这个算法的思想,只要大家掌握了思想就可以写出自己个性的代码
#include<stdio.h>
#define max(a,b) ( a > b ? a:b) // max函数
int ST[100][100];
int mn[100];
int a[100];
int main(void) // ST[i][j] 表里储存的是区间 [ i,i+2^j ) 里的最值,注意区间是左闭右开
{
int Find(int L,int R);
void Creat(int N);
int i;
int N = 10; // 初始化数组的大小,也可以通过键盘输入
for( i = 0; i < N; i++)
{
a[ i ] = i * 3; // 初始化数组,也可以通过键盘输入,这里为了更简单一点
}
Creat(N);
printf("%8d",Find(0,5));
return 0;
}
void Creat(int N)
{
int i,j;
mn[0] = -1;
for( i = 0; i < N; i++)
{
ST[i][0] = a[i]; // 初始化ST表
mn[i+1] = ( ( (i+1) & i ) == 0 ) ? mn[i] + 1 : mn[i] ; // 初始化mn数
// mn数组里储存的是2^n 中的n,如果不恰好等于整数,向下取整
}
for( j =0; (1 << j )<= N; j++) // 处理ST表,这里为了简单求的是区间的最大值,也可以求其他最值
{
for( i =0; i < N; i++) // (1 << j) 等于 2 的 j 次方
{
ST[i][j+1] = max( ST[i][j] , ST[i + ( 1 << j)][j]); // 状态转移方程,区间(i,i+2^j)的最值为
} // 区间(i,i+2^(j-1))和区间(i+2^(j-1),i+2^j)的最值
}
}
int Find(int L,int R) // 求区间[L,R]的最值函数
{
int L_num = L;
int R_num = R;
int k = mn[R-L]; // 利用mn数组求出区间最多可以分成多少个n, 2^n = 区间长度
return max(ST[L][k],ST[ R-(1<<k) + 1][ k ]) ; // 比较两个相交区间求出目标区间的最值
} // +1 是为了是左闭右开区间 变成左闭右闭