码迷,mamicode.com
首页 > 编程语言 > 详细

【RMQ】【Sparse_Table算法】

时间:2015-09-02 01:58:16      阅读:197      评论:0      收藏:0      [点我收藏+]

标签:

 

定义:

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。同理 F[1][1] = max(3,2) = 3, F[1][2]=max(3,2,4,5) = 5,F[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 void initRMQ(int n)
3 {
4     for(int i = 1; i <= n; i++) dp[i][0] = A[i];
5     for(int j = 1; j < 20; i++)
6         for(int i = 1; i <= n; j++)
7             dp[i][j] = max(dp[i][j-1], dp[i + (1 << (j-1))][j-1]);
8 }

    注意代码中循环的顺序,外层是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]);

int Log2[maxn];
void init()
{
    Log2[0] = -1; Log2[1] = 0;
    int i, j;
    for(i = 2, j = 0; i < maxn; i++)
    {
        if(i > (1<<(j+1))) j++;
        Log2[i] = j;
    }
}

int query(int L, int R)
{
    if(L > R) swap(L, R);
    if(L == R) return dp[L][0];
    int len = R-L+1;
    int k = Log[len]; //此处用的是Log2[]数组保存每个数的log值,当然也可以用下面的式子求得;
    //int k = (int)(log(des - src + 1.0) / log(2.0));
    return max(dp[L][k], dp[R-(1<<k)+1][k]);
}

 

【RMQ】【Sparse_Table算法】

标签:

原文地址:http://www.cnblogs.com/LLGemini/p/4777418.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!