RMQ问题原理及实现

写在前面

        首先我们要明白RMQ表示的是什么:Range Minimum/Maximum Query(区间最小值/区间最大值),这里我们主要介绍的一种算法是ST算法,他预处理的复杂度为O(nlogn),查询复杂度为O(1),主要基于的思想就是动态规划。

RMQ问题算法详解

        我们知道这个算法是基于动态规划来进行的,我们就要首先说一下状态转移方程,我们定义:

dp[i][j]:从位置i开始,长度为(2^j)中最小或者最大的数是什么,我们这里就先讨论最小值的算法,然后是我们的状态转移方程:

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

他的意思就是将[ i , i + (1<<j)]区间分成两个区间,一个是[i , i + (1 << (j-1))] ,我们设现在更新的长度为x,即x = 1 << (j-1),那么另一半就是 [i + x, i + (1 << j)].这就是我们的状态转移方程

    实现预处理

        我们知道当j = 0 时,表示长度为1时的最小值,那么也就是它本身,然后后面的再去更新dp内容

void init_RMQ(int n,int a[])
{
    for(int i=0;i<n;i++)
        dp[i][0]=a[i];//这里因为j为0,所及就是2的0次幂的最小值就是它本身!
    for(int j=1;(1<<j)<=n;j++)//这里只是枚举能够到达的长度!
        for(int i=0;i+(1<<j)-1<n;i++)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}

    查询操作

int RMQ(int L,int R,int a[])
{

    int k=0;
    while(1<<(k+1)<=R-L+1)   //我们要得到这个区间中的k的大小
        k++;//这里计算出需要2的几次方能够把这个范围包围起来,可能会有点小,所以就会有重复计算的部分,但是不会影响计算的速度,因为已经进行过预处理了!
    return min(dp[L][k],dp[R-(1<<k)+1][k]);
}

那么我们就得到了我们最终的程序

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;
const int MAXN = 1100;
int dp[MAXN][MAXN];     //d[i][j]:第i个数起,向后连续2^j个数中的最大值

///RMQ算法用于计算区间最小值,当然也能改成最大值!
void init_RMQ(int n,int a[])
{
    for(int i=0;i<n;i++)
        dp[i][0]=a[i];//这里因为j为0,所及就是2的0次幂的最小值就是它本身!
    for(int j=1;(1<<j)<=n;j++)//这里只是枚举能够到达的长度!
        for(int i=0;i+(1<<j)-1<n;i++)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}

int RMQ(int L,int R,int a[])
{

    int k=0;
    while(1<<(k+1)<=R-L+1)   //我们要得到这个区间中的k的大小
        k++;//这里计算出需要2的几次方能够把这个范围包围起来,可能会有点小,所以就会有重复计算的部分,但是不会影响计算的速度,因为已经进行过预处理了!
    return min(dp[L][k],dp[R-(1<<k)+1][k]);
}

int main()
{
    memset(dp,0,sizeof(dp));
    int a[]={3,4,5,6,7,8,9,0,3,4,5};
    init_RMQ(sizeof(a)/sizeof(a[0]),a);
    cout<<RMQ(5,7,a)<<endl;
}
如果有错误,欢迎大家在下面评论区评论,谢谢!

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值