RMQ 的ST算法 【模版】+【知识点】

ST算法

知识点 一
点我

知识点 二
点我

代码
给定一个序列,m次询问 【le,ri】 区间上的最值

#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <algorithm>  
#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<<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;  
}  

上面说过了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 <cstdio>
#include <cstring>
#include <algorithm>
#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<<j) <= k; j++)
    {
        for(int i = 1; i + (1<<j) - 1 <= N; i++)
        {
            Amax[i] = max(Amax[i], Amax[i + (1<<(j-1))]);//压缩
            Amin[i] = min(Amin[i], Amin[i + (1<<(j-1))]);
        }
    }
}
int query(char *op, int L, int R)
{
    int len = 0;
    while(1<<(len+1) <= R-L+1) len++;//求len值
    if(strcmp(op, "MAX") == 0)
        return max(Amax[L], Amax[R-(1<<len)+1]);
    else
        return min(Amin[L], Amin[R-(1<<len)+1]);
}
int main()
{
    while(scanf("%d%d%d", &N, &M, &k) != EOF)
    {
        for(int i = 1; i <= N; i++)
            scanf("%d", &A[i]);
        RMQ_init();
        char op[10];
        int x;
        while(M--)
        {
            scanf("%s%d", op, &x);
            printf("%d\n", query(op, x, x+k-1));
        }
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值