ST表求解RMQ问题

RMQRMQ 问题
RMQ(Range Minimum Query)RMQ(Range Minimum Query),范围最小值问题。具体表现为一下一类问题:

给出一个 nn 个元素的数组 A1,A2,…,AnA1,A2,…,An ,求解 min(l,r)min(l,r) : 计算 minmin{Al,Al+1,…,ArAl,Al+1,…,Ar}

RMQRMQ 问题有很多解法,其中较为快捷和简便的是 TarjanTarjan 的 Sparse−TableSparse−Table 算法,简称 STST 表。

Sparse−TableSparse−Table 算法基于倍增思想,整个算法由预处理和查询两部分组成,分别描述一下:

预处理

我们令 d(i,j)d(i,j) 为从 ii 开始的, 长度为 2j2j 的一段元素中的最小值。根据倍增思想,d(i,j)d(i,j) 可以通过 d(i,j−1)d(i,j−1) 和 d(i+2j−1,j−1)d(i+2j−1,j−1) 转移得到,具体操作就是对两个数取 minmin 。

没有接触过倍增思想的同学可能对这步表示有点难以理解,具体解释一下:

d(i,j)d(i,j) 表示的是从 ii 开始的长度为 2j2j 的一段元素中的最小值,区间右端点是 i+2j−1i+2j−1 。

d(i,j−1)d(i,j−1) 表示的是从 ii 开始的长度为 2j−12j−1 的一段元素中的最小值,区间右端点是 i+2j−1−1i+2j−1−1 。

d(i+2j−1,j−1)d(i+2j−1,j−1) 表示的是从 i+2j−1i+2j−1 开始的长度为 2j−12j−1 的一段元素中的最小值,区间右端点是 i+2j−1+2j−1−1=i+2j−1i+2j−1+2j−1−1=i+2j−1 。

现在就明显了,[i,i+2j−1][i,i+2j−1] 这段区间被划分成了了 [i,i+2j−1−1][i,i+2j−1−1] 和 [i+2j−1,i+2j−1][i+2j−1,i+2j−1] 两段区间,不重不漏,所以这样操作是可行的。

预处理的时间复杂度是 O(nlog2n)O(nlog2⁡n) 。

查询

有了刚才对预处理的讲述,查询部分应该不难想到,我们令 kk 为满足 2k≤R−L+12k≤R−L+1 的最大整数。则可知 k=log2R−L+1k=log2⁡R−L+1 。则以 LL 开头, 以 RR 结尾的两个长度为 2k2k 的区间合并起来就覆盖了 [L,R][L,R] 。由于是求范围最小值,有元素被重复计算也没问题。

则 Query(L,R)=min(d(L,k),d(R−2k+1,k))Query(L,R)=min(d(L,k),d(R−2k+1,k)) 。

查询的时间复杂度是 O(1)O(1) 。

由此可见,Sparse−TableSparse−Table 算法思想简单,好写,是求解 RMQRMQ 问题的首选算法。

具体实现的时候还要注意一点,每次用 pow(2,x)pow(2,x) 计算 2x2x 是非常浪费时间的。由于计算机内部使用的是二进制,我们可以用 (1<<x)(1<<x) 表示 2x2x 。

CodeCode
namespace RMQ {
void init(int n) {
for (int i = 1; i <= n; i++) d[i][0] = a[i];
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
}
int query(int l, int r) {
int k = log2(r - l + 1);
return min(d[l][k], d[r - (1 << k) + 1][k]);
}
}
————————————————
版权声明:本文为CSDN博主「Nekroz_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/diogenes_/article/details/80794838

猜你喜欢

转载自www.cnblogs.com/aprincess/p/11621523.html
今日推荐