ST表 详解(C语言描述)

    前面先给大家讲一下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 是为了是左闭右开区间 变成左闭右闭 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值