Rmq算法

RMQ问题(Range Minimum Query):

概念

对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。

求解方法:

朴素方法:

我们预处理阶段时间复杂度为:O(n)
我们查询阶段时间复杂度为:O(n)

线段树:

这是一个比较简单的线段树问题,咱们求解一段区间的最值,需要用到懒操作
详情请看:线段树

ST(Sparse Table)算法

  • 建立一个二维数组F[1…N][1…O(log2N)]的整数值
    在这个数组中F[i][j]表示范围[i,i+2^j -1]中的最大值
    (例如[2,5]中的最大值被计为F[2][2])。
    建立数组的过程可以在O(NlogN)时间内完成。
    使用动态规划的思想
void Initialise(vector<int> &Array)
{
    int Length = (int)Array.size();
    // 长度为 0 时,表示数据自身。
    for (int i = 0; i < Length; ++i) F[i][0] = Array[i];
    for (int j = 1; (1 << j) <= Length; ++j)
        for (int i = 0; i + (1 << j) - 1 < Length; ++i)
            F[i][j] = min(
                F[i][j - 1], F[i + (1 << (j - 1))][j - 1]
            ); // 分成长度相等的两段
}
  • 查询
    当我们需要计算一个区间中的最大值(例如 [3,8]时),我们先求出这个区间的长度(即 8 - 3 + 1 = 6)。
    然后我们算出不大于它的最大2^k值的指数(即 2)。我们要查询分别以3开头,和以 8 结尾的两个长度为2^2的区间的最大值。
    显然F[3][2]和 F[5][2]也就是([3,6]和[5,8]的最大值就是[3,8]的最大值。查询的时间是常数,代码如下:
int Query(int Left, int Right)
{
    int i = 0;
    while (1 << (i + 1) <= Right - Left + 1) ++i;
    return min(
        F[Left][i], F[Right - (1 << i) + 1][i]
    );
}

感谢:
1. ST算法:https://zh.wikipedia.org/wiki/%E8%8C%83%E5%9B%B4%E6%9C%80%E5%80%BC%E6%9F%A5%E8%AF%A2
2. RMQ问题简介:
http://kmplayer.iteye.com/blog/575725

你可能感兴趣的:(模板)