RMQ算法详解之ST

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

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

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

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

Amin[i][j]为从i开始,长度为2^j的区间里面的最小值 

预处理

首先我们对于区间的预处理是将长度为2^j的区间分成两个长度为2^(j-1)的区间【就像二分那样】

然后得到诸如    i……i+2^(j-1)-1   和   i+2^(j-1)……i+2^j-1   的区间

便能得到转移方程【顺序与j正相关】

maxx[i][j]=max(maxx[i][j-1],maxx[i+2^(j-1)][j-1])

minn[i][j]=min(minn[i][j-1],minn[i+2^(j-1)][j-1])

 

查询方程

           MAX(L,R)表示查询区间[L,R]内的最大值;

           MIN(L,R)表示查询区间[L,R]内的最小值。

我们有必要寻找一个合适的最小的k使得 以L开始的长度为2^k的区间 与 以R结束的长度为2^k的区间 刚好能够覆盖整个[L,R]区间。

可以如此计算k值:从0递增直到2^(k+1)>R-L+1

 

code:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 10010
#define inf 0x3f3f3f3f
int a[maxn],n,m,maxx[maxn][50],minn[maxn][50];        //即文中所表意思
void RMQ_in() {
    for(int i = 1; i <= n; i++)
        maxx[i][0] = minn[i][0] = a[i];
    for(int j = 1; (1<<j) <= n; j++) {
        for(int i = 1; i+(i<<j)-1 <= n; i++) {
            maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);
            minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
        }
    }
}

int query(int *op,int l,int r) {                    //判断求最大或最小
        //k的一种解法
    int k=0;
    for(; (1<<(k+1)) < (r-l+1); k++);
    if(strcmp(op, "MAX") == 0)
        return max(maxx[L][k],maxx[R-(1<<k)+1][k]);
    else
        return min(minn[L][k],minn[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_in();
        char op[10];
        int x,y;
        for(int i = 1; i <= m; i++) {
            scanf("%s%d%d",op,&x,&y);
            printf("%d\n",query(op,x,y));
        }
    }
    return 0;
}


 

 

但是各位刷题大佬们肯定有所发现,有些RMQ类型的题目是制定询问区间长度为k的,此时如果还想像上面一样解答,怕是会浪费极大的空间,所以优化是必不可少的。↓↓↓↓

↓↓↓↓↓↓

 

优化

我们只需要长度为k的代码,这就是说,我们并不需要使用二维数组来储存,而是只使用一维数组,

那么只需要定义一维数组 maxx[i] 来表示以i为起始,长度近似为k的区间的最大值就行了

 

这样的话,我们只需要每次长度递增到k就行了。

当然对于查询的话只需要提取就可以了。

code:

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#define maxn 10010
using namespace std;
int a[maxn],n,m,k,t,maxx[maxn],minn[maxn];
void RMQ_in() {
    for(int i=1; i <= n; i++)
    maxx[i]=minn[i]=a[i];
    for(int j=1; (1<<j) <= k; j++) {
        for(int i=1; i+(1<<j)-1 <= n; i++) {
            maxx[i]=max(maxx[i],maxx[i+(1<<(j-1))]);
            minn[i]=min(minn[i],minn[i+(1<<(j-1))]);
        }
    }
}

int query(char *op,int l,int r,int t) {
    if(strcmp(op,MAX) == 0)
        return max(maxx[l],maxx[r-(1<<t)]);
    else
        return min(minn[l],minn[r-(1<<t)]);
}

int main() {
    while(scanf("%d%d%d",&n,&m,&t) != EOF) {
        char op[10];
        for(t=0; (1<<(t+1)) <= k; t++);
        for(int i=1; i <= n; i++) scanf("%d",&a[i]);
        for(int i=1; i <= m; i++) {
            int x;
            scanf("%s%d",op,&x);
            printf("%d\n",query(op,x,x+k-1,t));
        }
    }return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值