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 + 1)大R-L+1为止。

 

找到k后,查询操作有

return max(Amax[L][k], Amax[R-(1<<k)+1][k]);//返回区间最大值

return min(Amin[L][k], Amin[R-(1<<k)+1][k]);//返回区间最小值

 

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

=> 我们只需要一维数组定义如下:Amax[i] 存储的是以序列A[i]开始的,长度为k的区间里面的最大值。Amin[i]同样如此。状态转移方程:

 

Amax[i] = max(Amax[i], Amax[i + (1<<(j-1))]);//压缩 

Amin[i] = min(Amin[i], Amin[i + (1<<(j-1))]); 

 

//RMQ 区间最小值问题

 

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<<j) <= N; j++)

    for(int i = 1; i + (1<<j)-1 <= N; i++){

            Amax[i][j] = max(Amax[i][j-1], Amax[i+(1<<(j-1))][j-1]);

            Amin[i][j] = min(Amin[i][j-1], Amin[i+(1<<(j-1))][j-1]);

        }

}

int query(char *op, int L, int R){

    //k值求法有两种

    int k = 0;

    while((1<<(k+1)) <= R-L+1) k++;

    //int k = (int)(log(double(R - L + 1))/log((double)2));

    if(strcmp(op, "MAX") == 0)

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

    else

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

}

int main(){

    while(scanf("%d%d", &N, &M), N||M){

        for(int i = 1; i <= N; i++)

            scanf("%d", &A[i]);

        RMQ_init();

        char op[10];

        int x, y;

        while(M--){

            scanf("%s%d%d", op, &x, &y);

            printf("%d\n", query(op, x, y));

        }

    }

    return 0;

}

 

posted @ 2017-12-28 13:22  proscientist  阅读(145)  评论(0编辑  收藏  举报