poj3264-RMQ问题的ST算法

什么是RMQ问题

RMQ(Range Minimum/Maximum Query),即查询给定区间的最值。
问题描述:对于给给定的数组A[1…n],回答若干次查询文件RMQ(i,j),即返回数组A[i…j]的最大值/最小值
问题分析:该问题可以使用朴素查询法,也可以用线段树,ST算法,RMQ与LCA互相转换。这里我们分析高效的稀疏表算法(Sparse Table, ST算法)。

ST算法

ST算法是求解RMQ问题的经典算法,预处理时间为O(nl0gn),查询时间为O(1)

预处理部分

预处理部分使用DP(动态规划)解决。如果要求解的数列为A[]={2,1,4,6,8,3,5,9,6},需要访问最小值。
状态空间的定义为:F[i,j]表示第i个数起,连续的2^j个数中的最大值.比如F[1,0]=min{A[1]}=2,F[2,2]=max{A[2],A[3],A[4],A[5]} = 1。显然初始状态为F[i,0]=A[i].
状态转移方程:因为F[i,j]中元素的个数为2^j个,因此我们考虑把其分为两段。从ii+2^(j-1)-1和从i+2^(j-1)i+2^j-1。那么状态转移方程为:F[i,j]=min(F[i,j-1], F[i+2^(j-1), j-1])

预处理的代码如下:

void RMQ(int n)
{
    for (int j = 1; j < 32; ++j)
    {
        for (int i = 1; i <= n; ++i
        {
            if (i + (1 << j) -1 <= n)//不能超过n
                dp[i][j] = min(dp[i][j-1], dp[i + (1<<(j-1))][j-1]);
        }
    }
}
查询部分

查询区间为[i,j],那么我们只需要找到覆盖这个闭区间的最小的幂即可(注意这里第可以有重复的,比如查询[3,4,5,6,7,8],需要查[3,4,5,6][5,6,7,8]).
区间长度为:j-i+1,那么区间的最小幂为k=log2(j-i+1),则有RMQ(A,i,j)=min(dp[i,k][j-2^k+1], k)
比如说,要求区间为[3,8],k=log2(8-3+1)=2。那么RMQ(A,3,8) = min(dp[3][2], dp[5][2])

例题 poj3264

题目大意:对于给定的数组A[],给定一个区间范围,返回这个区间最大值与最小值的差。
解题思路:RMQ问题。

#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

#pragma warning(disable:4996)

const int MAX_N = 50000 + 10;
const int MAX_L = 20;//j的长度,大约可以访问1000,000

int A[MAX_N];
int dp_min[MAX_N][MAX_L];
int dp_max[MAX_N][MAX_L];

void init(int n)
{
    for (int k = 1; k <= n; ++k)
    {
        dp_min[k][0] = A[k];
        dp_max[k][0] = A[k];
    }
    for (int j = 1; j < MAX_L; ++j)
    {
        for (int i = 1; i <= n; ++i)
        {
            if (i + (1 << j) - 1 <= n)
            {
                dp_min[i][j] = min(dp_min[i][j - 1], dp_min[i + (1 << (j - 1))][j - 1]);
                dp_max[i][j] = max(dp_max[i][j - 1], dp_max[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
}

int RMQ(int i, int j)
{
    int k = (int)(log(double(j - i + 1)) / log(2.0));
    return max(dp_max[i][k], dp_max[j - (1 << k) + 1][k]) - 
        min(dp_min[i][k], dp_min[j - (1 << k) + 1][k]);
}

int main()
{
    freopen("poj3264.txt", "r", stdin);
    int N, Q;
    scanf("%d %d", &N, &Q);
    for (int i = 1; i <= N; ++i)
        scanf("%d", &A[i]);
    init(N);
    for (int i = 1; i <= Q; ++i)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        printf("%d\n", RMQ(a, b));
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值