RMQ - 求区间最值

RMQ

RMQ算法,是一个快速求区间最值的离线算法,预处理时间复杂度O(n*log(n)),查询O(1)。

概念:RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。

预处理

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

例如:A数列为3 2 4 5 6 8 1 2 9 7
首先初始化F[i,0]=A[i],F[1,0]代表从第一个数,长度为20的最大值,即为3。同理F[1,1]=max{3,2}=3,F[1,2]=max{3, 2, 4, 5}=5。

推导状态转移方程
将F[i,j]平均分成两份(F[i,j]一定是偶数),从i到i+2j-1-1为一段,i+2j-1到i+2j-1为一段。(长度都为2j-1
例如:当i=1,j=3时就是3,2,4,5和6,8,1,2,F[i,j]就是这两段中各自的最大值。
由此状态转移方程为:F[i,j]=max{F[i,j-1],F[i+2j-1,j-1]}。

代码如下:

void RMQ(int num)//num是数量
{
    for(int i=1; i<=num; i++)
    {
        dp_min[i][0]=arr[i];
        dp_max[i][0]=arr[i];
    }
    for(int j=1; (1<<j)<=num; j++)
        for(int i=1; i+(1<<j)-1<=num; i++)
        {
            dp_max[i][j]=max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
            dp_min[i][j]=min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
        }
}
void query(int l,int r,int op) //op=1 查找最大值,op=0,查找最小值
{
    int k=log2(r-l+1);
    if(op)
        return max(dp_max[l][k],dp_max[r-(1<<k)+1][k]);
    else
        return min(dp_min[l][k],dp_min[r-(1<<k)+1][k]);
}
posted @ 2019-11-07 19:55  Cherish486  阅读(46)  评论(0编辑  收藏  举报