数据结构之求解RMQ问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Q1410136042/article/details/82660588

RMQ,即range minimum queuy,范围最小值查询,一般朴素算法查询单个区间是O(n),查询m个就是O(m*n) ,这里要说的Sparse-Table算法,需要O(nlog n)的预处理,O(1)的单次查询,在查询次数很多的时候就能体现更好的优越性。而且,最重要的是这个算法写法简单,理解方便~~

令d(i,j)表示从i开头的,长度为2^j(本文中^都表示次方而不是异或)的区间内的最小值,则可以使用递推来求d(i,j):

d(i,j)=min(d(i,j-1), d(i+2^{j-1}, j-1))

这不难理解啊,直接将2^j个元素分两半了,然后取左边2^(j-1)个元素的最小值和2^(j-1)个元素的最小值的最小值~显然这个递推是O(1)的,然后i有n个,由于2^j <= n,也就是 j <= log n,所以d数组有不超过nlog n个元素,所以预处理复杂度为O(nlog n)

然后查询,如何从d里面O(1)地查到任意区间[L, R]的最小值呢?

实际上,我们也可以像上面递推一样,分成两个部分,但是由于区间长度n = R-L+1不一定是2的幂,所以可能不能恰好分成两部分——但是,值得庆幸的是这是求最小值,并不需要保证两个区间不能有交,只需要保证两个区间并起来是[L,R]即可~也就是说只要把[L,R]分成俩区间[L, L+2^k-1]和[R-2^k+1, R]即可~然后怎么求这个k呢?显然是满足2^k <= n的最大的k就好了啊,也可以预处理出来~

代码:

#include<vector>
#include<cmath>
#include<algorithm>
#define rgt register int

using namespace std;
const int maxn = 10010;             // 最大元素个数
int d[maxn][(int)log(maxn)+1];      // d数组元素个数不超过nlog n,而每一个都可以在常数时间计算完毕
int K[maxn];                        // K[i]为满足(1<<k) <= i的最大整数,K[0]无意义
void initRMQ(const vector<int>& A)  // 对A进行预处理
{
    int n = A.size();
    for(rgt i = 0; i < n; ++ i)
        d[i][0] = A[i];
    for(rgt j = 1; (1<<j) <= n; ++ j) //O(nlog n)处理d数组
        for(rgt i = 0; i+(1<<j)-1 < n; ++ i)
            d[i][j] = min(d[i][j-1], d[i+(1<<(j-1))][j-1]);

    int p = 1;
    K[0] = -1;
    for(int i = 1; i <= n; ++ i)
    {
        if(i == p)
        {
            K[i] = K[i-1] + 1;
            p <<= 1;
        }
        else
            K[i] = K[i-1];
    }
}

int RMQ(int L, int R)
{
    int k = K[R-L+1];
    return min(d[L][k], d[R-(1<<k)+1][k]);
    //从L开始、以R结尾的1<<k的区间合起来覆盖了[L,R],由于是取最小值,有元素重复没关系~
}

 

猜你喜欢

转载自blog.csdn.net/Q1410136042/article/details/82660588