• RMQ原理及实现


      RMQ(Range Minimum/Maximum Query),区间最值查询问题,是指:对于长度为n的数列A,回答若干次询问RMQ(i,j),返回数列A中下标在区间[i,j]中的最小/大值。

      这里介绍Tarjan的Sparse-Table算法,预处理时间为O(nlogn),但查询只需要O(1),并且常数很小,算法也很容易写出。

     1)预处理:

      设A[i]是要求区间最值的数列,d[i, j]表示从第i个数起连续2^j个数中的最小值。(DP的状态)

      显然d[i][0]的值就是A[i](DP初值),我们把d[i,j]平均分成两段(因为d[i,j]一定是偶数个数字),从 i 到i + 2 ^ (j - 1) - 1为一段,i + 2 ^ (j - 1)到i + 2 ^ j - 1为一段(长度都为2 ^ (j - 1))。于是我们得到了状态转移方程d[i, j]=min(d[i,j-1], d[i + 2^(j-1),j-1]),代码实现如下(这里使用lrj蓝书代码):

    1 void RMQ_init(const vector<int> &A) {
    2     int n = A.size();
    3     for(int i = 0; i < n; ++i) d[i][0] = A[i];
    4     for(int j = 1; (1 << j) <= n; ++j) 
    5         for(int i = 0; i + (1 << j) - 1 < n; ++i) 
    6             d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
    7 }

    2)查询:

      假如我们需要查询的区间为(i,j),那么我们需要找到覆盖这个闭区间(左边界取i,右边界取j)的最小幂(可以重复,比如查询1,2,3,4,5,5不是2的任意次方,但我们可以查询1234和2345)。

      这个查询长度我们取范围小于等于区间长度的最大(2^k),这样我们查询i到 i +(2^k)与j - (2^k) + 1到j的值,取二者最小值即可,代码实现如下:

    1 int RMQ(int L, int R) {
    2     int k = 0;
    3     while((1 << (k + 1)) <= R - L + 1) ++k;
    4     return min(d[L][k], d[R - (1 << k) + 1][k]);
    5 }
    版权声明:该博客版权归本人所有,若有意转载,请与本人联系
  • 相关阅读:
    JVM垃圾收集器以及内存分配
    VisualVM工具的使用
    jstack的使用
    内存溢出的定位与分析
    JVM的内存模型
    JVM运行参数
    kafka-高效读写数据+zookeeper作用+事务
    重定向机制
    HTTP协议、时间戳
    TCP常见面试题 
  • 原文地址:https://www.cnblogs.com/fan-jiaming/p/9739281.html
Copyright © 2020-2023  润新知