数列区间最大值 RMQ问题(ST算法模板)

问题:

输入一串数字,给你 M个询问,每次询问就给你两个数字 X,Y,要求你说出 X 到 Y 这段区间内的最大数。

输入格式

第一行两个整数 N,M 表示数字的个数和要询问的次数;
接下来一行为 N 个数;
接下来 M 行,每行都有两个整数X,Y 。

输出格式

输出共 M 行,每行输出一个数。

样例

样例输入

10 2
3 2 4 5 6 8 1 2 9 7
1 4
3 8

样例输出

5
8

ST算法:ST算法是解决RMQ(区间最值)问题,它能在O(nlogn)的时间预处理,然后O(1)回答。其原理是倍增,h[i][j]表示从i位起的2^j个数中的最大数,即[i,i+2^j-1]中的最大值。

流程:h[i][0]表示从 i 开始到2^0个数之间中的最大值,只能是 a[i],故 h[i][0]=a[i]。
对于任意的 h[i][j],我们分成两段相等长度的数列来看,[ i , i+2^(j-1)-1] 和 [ i+2^(j-1) , i+2^j-1 ],分别对应 h[i][j-1] 和 h[i+(1<<j-1)][j-1]。遍历一遍之后直接求区间最大值即可。

例如 x 到 y 之间的最大值。

k=log2(y-x+1);//区间的长度

max(h[x][k],h[y-(1<<k)+1][k]);第1个区间:x 到 x+(1<<k)-1;第2个区间:y-(1<<k)+1到y。

可以代数尝试一下,推一下就明白了我代的数是x=2,y=20,k=4,也就是(2~17)和(5~20)这两个区间。

模板代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#define N 100005
#include<algorithm>
using namespace std;
const int M=25;
int h[N][M],a[N];//h[i][j]表示从i位起的2^j个数中的最大数
int main()
{
    int n,m,x,y;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        h[i][0]=a[i];//h[i][0]表示[i,i]中的最大值,只能是a[i],故h[i][0]=a[i]。
    }
    for(int j=1; j<=log2(n); j++)
        for(int i=1; i+(1<<j)-1<=n; i++)//注意i的右端点为i+(1<<j)-1,不能越界
            h[i][j]=max(h[i][j-1],h[i+(1<<(j-1))][j-1]);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&x,&y);
        int k=log2(y-x+1);
        printf("%d\n",max(h[x][k],h[y-(1<<k)+1][k]));//第1个区间:x 到 x+(1<<k)-1;第2个区间:y-(1<<k)+1 到 y 。
                                                       //求出这两个的最大值即可
    }
    return 0;

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值