• 【RMQ】【Sparse_Table算法】


     摘自网友,具体哪个忘记了,抱歉~

    定义:

    RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:

      对于长度为n的数列A,回答若干询问RMQ(A,i,j) (i,j<=n),返回数列A中下标在i,j之间的最小/大值。

    此类问题的解决方法有很多,暴力(当然也就说说,基本没有出题的会让你暴过去)、线段树(复杂度为O(nlogn))、以及一个非常有名的在线处理RMQ问题的算法 -- Sparse_Table算法,简称ST算法,该算法能在进行O(nlogn)的预处理后达到查询O(1)的效率。主要思想为dp。

    (一)预处理,DP

    设A[i]是要求区间最值的数列,dp[i][j]表示从第i个数起连续2j个数中的最大值

    例如有A[10] = 3 2 4 5 6 8 1 2 9 7;

    初值:

    dp[1][0]表示第1个数起,长度为20=1的最大值,即元素3。同理 dp[1][1] = max(3,2) = 3, dp[1][2]=max(3,2,4,5) = 5,dp[1][3] = max(3,2,4,5,6,8,1,2) = 8;

    并且我们可以容易的看出dp[i][0] = A[i]。

    状态转移方程:

    我们把dp[i][j]分成长度相同且为2j-1的两段,一段为dp[i][2j - 1], 一段为dp[i+2j - 1][2j - 1]。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。

    dp[i,j]就是这两段各自最大值中的最大值。于是我们得到了状态转移方程:

      dp[i][j] = max(dp[i][j-1], dp[i + 2(j-1)][j-1];

     

    1 /*预处理->O(nlogn)*/
    2 int dp[maxn][20];
    3 void initRMQ(int n)
    4 {
    5     for(int i = 1; i <= n; i++) dp[i][0] = A[i];
    6     for(int j = 1; j <= 20; j++)
    7         for(int i = 1; i + (1 << (j-1)) <= n; i++)
    8             dp[i][j] = max(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
    9 }

      注意代码中循环的顺序,外层是j,内层是i!!

    (二)查询

    若查询区间为A[i, j],那么我们需要找到覆盖这个闭区间(左边界取i,右边界取j)的最小幂(可重叠,比如查询A[5,9],我们可以查询A[5678]和A[6789])。

    因为这个区间的长度len = j - i + 1,所以我们可以取k=log2(len),则有:RMQ(A, i, j) = max{ dp[i][k], dp[j-2k+1][k] }。

    例如,查询区间[2,8]的最大值,k = log2(8 - 2 + 1) = 2,即求max( dp[2][2],dp[8 - 2 ^ 2 + 1][2]) = max(F[2, 2],F[5, 2]);

     1 int Log2[maxn];
     2 void init()
     3 {
     4     Log2[0] = -1; Log2[1] = 0;
     5     int i, j;
     6     for(i = 2, j = 0; i < maxn; i++)
     7     {
     8         if(i > (1<<(j+1))) j++;
     9         Log2[i] = j;
    10     }
    11 }
    12 
    13 int query(int L, int R)
    14 {
    15     if(L > R) swap(L, R);
    16     if(L == R) return dp[L][0];
    17     int len = R-L+1;
    18     int k = Log2[len]; //此处用的是Log2[]数组保存每个数的log值,当然也可以用下面的式子求得;
    19     //int k = (int)(log(R - L + 1.0) / log(2.0));
    20     return max(dp[L][k], dp[R-(1<<k)+1][k]);
    21 }

    RMQ常常应用在字符串问题里.

  • 相关阅读:
    tyvj4751 NOIP春季系列课程 H's Problem (树状数组)
    卡牌分组([AtCoder ARC073]Ball Coloring)
    bzoj1036 [ZJOI2008]树的统计Count (树链剖分+线段树)
    bzoj2287 POJ Challenge 消失之物(背包)
    不能建立引用数组
    CString和string的区别
    防止应用程序重复启动
    public,protected,private
    ATL
    c++头文件中定义全局变量
  • 原文地址:https://www.cnblogs.com/LLGemini/p/4777418.html
Copyright © 2020-2023  润新知