zoukankan      html  css  js  c++  java
  • 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 }
    版权声明:该博客版权归本人所有,若有意转载,请与本人联系
  • 相关阅读:
    WordPress研究心得
    Java之生成Pdf并对Pdf内容操作
    Java之生成条形码、PDF、HTML
    Redis口令设置
    Redis启动问题解决方案
    网狐6603手机棋牌游戏源码.rar
    LNK1179 无效或损坏的文件: 重复的 COMDAT“_IID_IDispatchEx”
    c++转C#
    error LNK1281: 无法生成 SAFESEH 映像 LNK2026 模块对于 SAFESEH 映像是不安全的 VS2015 /win10
    当两行的数据一样时,要删除一行的正则表达式解决办法。
  • 原文地址:https://www.cnblogs.com/fan-jiaming/p/9739281.html
Copyright © 2011-2022 走看看