LXH的算法学习

ST算法——RMQ问题(区间最值问题)

引子:
RMQ 的英文是 Range Maximum(Minimum) Query,翻译过来其实就是区间求最值的意思。问题描述:对于长度为 n 的数列 A,回答若干询问 RMQ(A, i, j)(i, j <= n),返回数列A中下标在 [i, j] 里的最小(大)值。

因为可能需要海量的去查询操作,如果一个一个去找最大值那么时间复杂度将会巨大!!所以可以采用倍增的思想去提前预处理,这样时间复杂度就会使O(nlogn),然后以O(1)的时间去查询。

~~解释:F[i,j]表示在数列中,子区间在[i,i+2^j-1] 里面的最大值,也就是从i开始的2的j次方个数里面的最大值。递推边界时F[i,0]=a[i],即[i,i]的最大值。
在递推时我们把子区间的长度成倍增长,有公式F[i,j]=max(F[i,j-1],f[i+1<<(j-1),j-1],即长度为2的j次幂的子区间的最大值是左右两半长度为2^(j-1)的子区间的最大值中较大的一个
代码如下:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int a[1e6+5],f[1e6+5][20];
void ST_prework(int n)
{
    for(int i=1;i<=n;i++)  f[i][0]=a[i];   //f[i][j]表示在区间[i,i+2^j]最大值
    int t=log(n)/log(2)+1;
    for(int j=1;j<t;j++)                  //二进制倍增:主题是以二进制倍增的思想
        for(int i=1;i<=n-(1<<j)+1;i++)    //将长度为2^j的子区间的最大值是左右两半子区间长度为2^(j-1)的子区间的最大一个
            f[i][j]=max(f[i][j-1],f[i+1<<(j-1)][j-1]);
        
}
int ST_query(int l,int r)
{
    int k=log(r-l+1)/log(2);
    return max(f[l][k],f[n-(1<<k)+1][k]); //计算一个k,使得2^k<=r-l+1<2^(k+1)
}                                         //使得2^k小于区间长度得前提下得最大得k,那么从l开始得2^k个数和以r结尾得2^k个数一定比包含了整个区间[l.r]
int main()
{
    int n,m;
    cin>>n;       
    for(int i=1;i<=n;i++)
        cin>>a[i];
    cin>>m;            //输入访问次数 
    ST_prework(n);     //对数组进行一个预处理,时间复杂度O(nlogn)
    while(m--)
    {
        int l,r;
        cin>>l>>r;
        cout<<ST_query(int l,int r);
    }
    return 0;
}
/*RMQ 的英文是 Range Maximum(Minimum) Query,翻译过来其实就是区间求最值的意思。
问题描述:对于长度为 n 的数列 A,回答若干询问 RMQ(A, i, j)(i, j <= n),返回数列A中下标在 [i, j] 里的最小(大)值。
在这个问题中,我们需要关注的是查询操作,查询可能是海量的。
所以如果我们对数据进行快速的预处理,然后在外面处理后的数据结构中进行快速查询,其实就是最理想的状态。
时间复杂度O(nlogn) 然后再以O(1) 时间去在线回答
*/

PS:这算法看了我好长时间,果然什么东西还是得动手去写代码,光看是没用的,还是得看你去写然后去理解这样才能真正去理解!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值