RMQ (Range Minimum/Maximum Query)算法

6 篇文章 0 订阅

RMQ算法是一种查找一个区间最值的算法,当然是有Q次询问,如果只询问一次,当然直接遍历就好了,

如果是询问很多次,这时就需要RMQ算法了。

RMQ算法

RMQ算法用的是DP求解, 预处理是nlogn的,查询是O(1)。

A[i]表示要查询的数列,F[i,j]表示从i开始2^j个数中最大的那一个。

例如:            3 ,4 ,5,23,1,12,7

F[1,0]就表示从3开始的1个数,就是3本身,F[2,2]表示从4开始2^2个数,即4,5,23,1这四个数中最大值23.

那么DP的初始状态是什么?

就是每个数的最大值是其本身,即F[i,0],把F[i,0]先求出来就是DP的初态

那么DP的状态转移方程呢?

通过二分的思想很容易得出,F[i,j]=max(F[i,j-1],F[i+(1<<(j-1)),(j-1)].

即把从i开始的2^j个数分成两个区间,分别是 [ i,i+2^(j-1)-1 ]和[ i+2(j-1),i+2^j-1],找出这两个区间中最大值进行比较。

void rmq(int n)
{
    for(int i=1;i<20;i++)
        for(int j=1;j+(1 << i)-1<=n;j++)    //j代表从j开始分为(j,j+2^(i-1)-1),(j+2^(i-1),j+2^i)这两个区间比较
        {
            maxnum[j][i]=max(maxnum[j][i-1],maxnum[j+1<<(i-1)][i-1]);
            minnum[j][i]=min(minnum[j][i-1],minnum[j+1<<(i-1)][i-1]);
        }
}

需要注意的问题:

1、注意for循环的嵌套,不能把j那一层放到外面,如果放到外面,就成了先求

  F[1,1],F[2,1],F[3,1]......

给出一个序列:3 ,4 ,5,23,1,12,7

F[1,1]=4,F[2,1]=5,F[3,1]=23,乍一看好像也没什么问题,接下来继续循环

F[1,2]=4,F[2,2]=5,F[3,2]=23,应该看出来问题来了, 找从i开始的4个数,只有前一半是更新出最值来了,后一半还是0.

所以不能这样循环。必须先把每个位置的值都从j=1(2个数的最值)开始依次更新,再是j=2(4个数的最值),3(8个数的最值)......

2、注意一下<< 和+,-运算符的优先级(在这踩了好长时间的坑。。。)

例如 1+2<<2

答案是3*2*2=12,所以想要表达成我们想要的算式则是:1+(2<<2).

查询过程

可以看成指数的逆运算(我是这么理解的),例如[l,r]这个区间,一共有r-l+1个数,那么就是k=log2(r-l+1),

那么要求的最大值就是 max([l,k],[r-(1<<k)+1,k],

最小值同理。

while(q--){
        scanf("%d%d",&l,&r);
        int k=(int)(log(r-l+1.0)/log(2.0));
        int maxn=max(maxnum[l][k],maxnum[r-(1<<k)+1][k]);
        int minx=min(minnum[l][k],minnum[r-(1<<k)+1][k]);
        printf("%d\n",maxn-minx);
    }

例题:poj3264

题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值