RMQ 之 ST算法的使用 【总结】 【附带求固定区间长度的一维技巧】


RMQ问题:区间最小值问题(也可以解决区间最大值问题)


解决算法:ST (Sparse - Table算法,基于动态规划求区间最值的算法) 



ST算法分为预处理和查询两部分 

首先定义数组:我们用定义 Amax[i][j] 为从 i开始的,长度为2^j的区间里面的最大值, Amin[i][j]为从i开始,长度为2^j的区间里面的最小值 





一:预处理如下


我们可以将一段长度为2^j的区间分成两段长度都为2^(j-1)的相同区间 
 
区间1 为  i.....i+2^(j-1)-1   区间2为  i+2^(j-1).....i+2^j-1 
 
得到状态转移方程(由长度递增推出)


Amax[i][j] = max(Amax[i][j-1], Amax[i+2^(j-1)][j-1])
Amin[i][j] = max(Amin[i][j-1], Amin[i+2^(j-1)][j-1])边界条件为F[i][0]=A[i]. 
  
二:查询如下


MAX L R 表示要查询[L, R]区间的最大值
MIN L R 表示要查询[L, R]区间的最小值


我们需要找到这样一个k值,使得从L开头的一个长度为2^k的区间 和 以R结尾的长度为2^k的区间 包括了整个[L, R]


k值计算有两种方案  1,k =(int) log2( R-L+1)  2,k值从0开始递增 直到2^k大于 R-L+1 为止。
 
找到k后,查询操作有


return max(Amax[L][k], Amax[R-(1<

return min(Amin[L][k], Amin[R-(1<



实际问题


给出一个N个数的序列A[] 和M次查询,查询如下:MAX x y 求区间[x, y]的最大值,MIN x y 求区间[x, y]的最小值。


代码:


#include 
#include 
#include 
#include 
#define MAXN 10010
#define INF 1000000000
using namespace std;
int A[MAXN];
int N, M;
int Amax[MAXN][50];//Amax[i][j]表示从i开始的,长度为2的j次方的区间里面的最大值
int Amin[MAXN][50];//Amin[i][j]表示从i开始的,长度为2的j次方的区间里面的最小值
void RMQ_init()
{
    for(int i = 1; i <= N; i++)
        Amax[i][0] = Amin[i][0] = A[i];
    for(int j = 1; (1<


上面说过了RMQ的使用——求序列任意区间长度的最大值以及最小值。


延伸:给出一个N个数的序列,并固定区间长度为k。M次查询 MAX x (MIN x) ——查询[x,x+k-1]的最大值(最小值)。


这时我们同样可以用上面的方法做。但这样会浪费很大的内存,因为对于这道题,我们只需要使用当区间长度为k时的信息(区间长度不为k的信息我们都用不到)。


思考一下?怎么优化?只需要使用当区间长度为k时的信息!


也就是说我们没有必要用二维数组来存储所有区间长度的可能取值。


我们只需要这样定义:Amax[ i ] 存储的是以序列A[ i ]开始的,长度为k的区间里面的最大值。Amin[ i ]同样如此。


这样的话,我们只需要按长度递增来推出区间长度为k时的情况 就ok了。


关于查询,也没什么变动,设置一个变量len。找到合适的len —— 使得以L开始的和以R结尾的两个长度为2^len的区间覆盖查询区间即可。


详看代码:


#include 
#include 
#include 
#define MAXN 1000000+10
using namespace std;
int N, M, k;
int A[MAXN];
int Amax[MAXN];
int Amin[MAXN];
void RMQ_init()
{
    for(int i = 1; i <= N; i++)
        Amax[i] = Amin[i] = A[i];
    for(int j = 1; (1<


你可能感兴趣的:(算法与有趣代码--记录,RMQ)