RMQ算法以及ST算法

一.RMQ的定义

RMQ的定义:

  RMQ是询问某个区间内的最值,主要以ST表的方式实现。

二.ST表

ST表(Sparse Table,稀疏表)是一种数据结构,用来解决区间内的一些问题(比如可以求区间最小值区间最大值区间GCD),ST表采用的倍增的思想,我们在使用ST表的时候,可以做到O(nlogn)的时间建表,做到用O(1)的时间去查询。

优点:ST表运行效率高。树状图支持求改操作。

缺点:ST表无法进行修改。树状图运行效率低。

三.模板题

思路

这一题是需要用到最大值和最小值两个RMQ,求出来后再相减就是答案。

我们用mx[i][j]来表示以i为起点,往后延伸2^j的这一段区间的最大值

同样的,用mn[i][j]来表示以i为起点,往后延伸2^j的这一段区间的最小值

预处理代码(状态转移方程)

那么状态转移方程怎么写呢?

以j为起点的往后延伸2^i的这一段区间可以被分为两部分

第一部分是以j为起点的往后延伸2^(i-1)的这一段区间

第二部分是以j+2^(i-1)为起点的往后延伸2^(i-1)的这一段区间

例如:

懂了吗?

预处理代码:

void qpzy()
{
    int x=log2(n);
    for(int i=1;i<=x;i++)
    {
        for(int j=1;j+a[i]-1<=n;j++)   //a[i]是指2^i
        {
            mx[j][i]=max(mx[j][i-1],mx[j+a[i-1]][i-1]);
            mn[j][i]=min(mn[j][i-1],mn[j+a[i-1]][i-1]);
        }
    }
    return;
}

查询代码

接下来就是查询代码了:

我们可以写一个函数来完成。从外面导入两个数字b和c,代表起始值和结束值

和预处理一样,也是分为两段来求max和min(具体看代码,不会的可以按照代码模拟一下,其实不是很难,要多模拟你就能懂)。

int zzxsb(int b,int c)
{
    int x=log2(c-b+1);
    int t1=max(mx[b][x],mx[c-a[x]+1][x]);
    int t2=min(mn[b][x],mn[c-a[x]+1][x]);
    return t1-t2;   //最大和最小两个相减
}

整体代码:

#include <bits/stdc++.h>
using namespace std;
int n;
int mx[50001][21],mn[50001][21],a[20],q;
void qpzy()
{
    int x=log2(n);
    for(int i=1;i<=x;i++)
    {
        for(int j=1;j+a[i]-1<=n;j++)
        {
            mx[j][i]=max(mx[j][i-1],mx[j+a[i-1]][i-1]);
            mn[j][i]=min(mn[j][i-1],mn[j+a[i-1]][i-1]);
        }
    }
    return;
}
int zzxsb(int b,int c)
{
    int x=log2(c-b+1);
    int t1=max(mx[b][x],mx[c-a[x]+1][x]);
    int t2=min(mn[b][x],mn[c-a[x]+1][x]);
    return t1-t2;
}
int main()
{
    scanf("%d%d",&n,&q);
    a[0]=1;
    for(int i=1;i<=15;i++)a[i]=a[i-1]*2;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        mx[i][0]=mn[i][0]=x;    //往后走2^0步就是它自己
    }
    qpzy();
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",zzxsb(x,y));
    }
    return 0;
}

四.练习题

思路

这一题跟上一题差不多,只需去掉mn[]数组和将max()改成__gcd()就行了。

代码

#include <bits/stdc++.h>
using namespace std;
int n;
int mx[50001][21],a[20],q;
void qpzy()
{
    int x=log2(n);
    for(int i=1;i<=x;i++)
    {
        for(int j=1;j+a[i]-1<=n;j++)
        {
            mx[j][i]=__gcd(mx[j][i-1],mx[j+a[i-1]][i-1]);
        }
    }
    return;
}
int zzxsb(int b,int c)
{
    int x=log2(c-b+1);
    int t1=__gcd(mx[b][x],mx[c-a[x]+1][x]);
    return t1;
}
int main()
{
    scanf("%d%d",&n,&q);
    a[0]=1;
    for(int i=1;i<=15;i++)a[i]=a[i-1]*2;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        mx[i][0]=x;
    }
    qpzy();
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",zzxsb(x,y));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值