RMQ算法小结(st实现)

rmq+st

RMQ简单来说就是求区间的最大值(最小值)。

举个例子:
1 -2 9 10 15 38 -9
这里有 7 个数(随便输的),RMQ就是用来查询这些数中的最大值(最小值),但是是区间的。比如查询 [1,3] 这个区间的最大值 就是 9 这个值 .

RMQ一般有三种实现方式:暴力、线段树优化、st(dp),其中以st算法复杂度最低,预处理复杂度为o(nlogn),区间查询复杂度为o(1),

最小值(Mininun):我们可以用F(i,j)表示区间[i,i+2^j-1]间的最小值。我们可以开辟数组来保存F(i,j)的值,例如:F(2,4)就是保存区间[2,2+2^4-1]=[2,17]的最小值。那么F(i,0)的值是确定的,就为i这个位置所指的元素值,这时我们可以把区间[i,i+2^j-1]平均分为两个区间,因为j>=1的时候该区间的长度始终为偶数,可以分为区间[i,i+2^(j-1)-1]和区间[i+2^(j-1)-1,i+2^j-1],即取两个长度为2^(j-1)的块取代和更新长度为2^j的块,那么最小值就是这两个区间的最小值的最小值,动态规划为:F[i,j]=min(F[i,j-1],F[i+2^(j-1),j-1]).同理:最大值就是F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1]).

现在求出了F[i,j]之后又是怎样求出最大值或者最小值的,怎么转换为o(1)这种算法的~这就是ST算法:

这个时候询问时只要取k=ln(j-i+1)/ln2即可,那么可以令A为i到2^k的块,和B为到2^k结束的长度为2^k的块;那么A,B都是区间[i,j]的子区间,所以即求A区间的最小值和B区间的最小值的最小值。这个时候动态规划为:RMQ(i,j)=min(F[i,k],F[j-2^k+1,k]);

RMQ+st模板:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <set>
#include<malloc.h>
#include <map>
#include <vector>
#include <queue>
#define ri(n) scanf("%d",&n)
#define oi(n) printf("%d\n",n)
#define rl(n) scanf("%lld",&n)
#define ol(n) printf("%lld\n",n)
#define rep(i,l,r) for(i=l;i<=r;i++)
#define rep1(i,l,r) for(i=l;i<r;i++)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int epg=10-8;
int a[10000+10];
int dp[10000+10][30];
int n;
void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<n;i++)
        dp[i][0]=a[i];
    int len=(int)(log((double)n)/log(2.0));
    //cout<<"len="<<len<<endl;
    for(int j=1;j<=len;j++)
    {
        for(int i=0;i<n;i++)
        {
            if((i+(1<<j)-1)<n)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
    //for(int i=0;i<n;i++)
        //for(int j=0;j<n;j++)
         //cout<<"dp["<<i<<"]["<<j<<"]="<<dp[i][j]<<endl;
}
int getmin(int i,int j)
{
    int k=(int)(log((double)j-i+1)/log(2.0));
    //cout<<"k="<<k<<endl;
    return min(dp[i][k],dp[j-(1<<k)+1][k]);
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    solve();
    int x,y;
    while(scanf("%d%d",&x,&y)!=EOF)
    {
        printf("%d\n",getmin(x,y));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值