【算法学习】RMQST算法入门

RMQ问题与ST算法
本文介绍了区间最值问题(RMQ)及其高效解决方案——ST算法。通过预处理DP数组实现O(1)时间复杂度的区间最值查询,详细解释了状态转移方程及其实现代码。

RMQ

RMQ问题是区间最值问题(Range Maximum / Minimum Query),即给出一个数组A1AnA1…An,给出一个询问区间l,rl,r,求出[Al,Ar][Al,Ar]的区间最值。
最暴力的做法肯定是循环,这样显然不够优秀,求解区间最值的问题,可以借助线段树。在询问只是询问时复杂度多一个log。对于没有修改操作的区间最值问题,ST的查询可以做到O(1),显然是一个更优秀的算法。

ST

其主要思想是利用dp来进行计算,计算方法和线段树的区间管理有些类似。
我们知道线段树求区间最值的思路,求其左区间的区间最值和右区间的区间最值,然后将答案进行合并即可。
然而此处利用dp,也求出类似数组,方面我们求解答案。

首先定义状态
dp[i][j]dp[i][j] 表示从i开始,后面2j2j个元素(包括i位置自身)中的最值

然后是初始状态
dp[i][0]=a[i]dp[i][0]=a[i]

状态转移方程
dp[i][j]=max(dp[i][j>>1],dp[i+(j>>1)][j>>1]dp[i][j]=max(dp[i][j>>1],dp[i+(j>>1)][j>>1] (也可以求解最小值)
可见思路是,在前半部分的区间最值和后半部分的区间最值中去最大(或最小的)。

通过如下的代码可以预处理dp数组

void getRMQ() {
    rep(i, 0, n - 1) dp[i][0] = a[i];
    for (int j = 1; (1 << j) <= n; ++j) {
        for (int i = 0; i + (1 << j) - 1 < n; ++i) {
            dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1) )][j - 1]);
        }
    }
}

注意iij的循环顺序,不可以颠倒,原因也很简单。首先需要对数组中每个i位置,求出其后面长度为 2j2j 的区间内最值。
如果此处颠倒了,求解前面的区间时,后面的区间还没有求解出来,这是不可以的。

对于询问就很简单了,直接用一步转移方程,求出区间最值就好了。

int RMQ(int l , int r) {
    if (l > r) return 0;
    int k = 0;
    while (1 << (k + 1) <= r - l + 1) k++;
    return max(dp[l][k], dp[r - (1 << k) + 1][k]);
}

代码中的k求解的是,询问的左端点ll最长可拓展到的位置,通过这样的转换,询问的区间会不会超出原本的r呢?
答案是不会的,因为另外区间的询问是从右端点rr开始减的,所以不会超出。

注意:

  1. 空间要开数据范围的log2n + 1倍
  2. 代码下标均为从0开始
  3. 预处理O(nlogn),询问O(1)
  4. 主要思想其实和线段树有点类似
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值