RMQ算法入门(dp)

本文深入讲解RMQ(区间最值查询)算法,一种用于高效查询数列中指定区间最大或最小值的算法。通过预处理数组,利用动态规划思想,实现快速查询。文章详细解释了算法原理,并附带POJ3264题目的解题思路及AC代码。
摘要由CSDN通过智能技术生成

在开始学LCA的时候,看别人的博客,知道了RMQ这个算法,在学LCA的时候会用的到,于是我就先把这个算法给学了,一百度,原来RMQ算法实现的功能,和线段树差不多,还有这个算法也是dp,dp无处不在啊,

RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。这两个问题是在实际应用中经常遇到的问题,下面介绍一下解决这两种问题的比较高效的算法。当然,该问题也可以用线段树(也叫区间树)解决,算法复杂度为:O(N)~O(logN),这里我们暂不介绍。
这个算法怎么实现的呢,其实本质上我感觉和线段树实现的而功能差不多,

这里我们都直接当最大值求了哈,其实最小值也一样。

我们先定义一个数组,dp[i][j],i代表的意思是从数组的第i个位置开始,j代表的是长度为2^j,所以dp[i][j]带表的意思就是数组的a[i]~a[i+2^j-1]    的最大值,这个数组的初始状态应该是什么呢,就是dp[i][0]=a[i],

刚开始我们可以先求长度为1的最大值,然后我们可以根据长度为1的最值求长度为2的,再求长度为4的,再求长度为8的,依次类推,

dp[i][j]    的长度一定是偶数,(这个很容易知道吧,这里就不证明了,)既然他是偶数那么我们就可以把这个dp[i][j]所表示的序列从中间给一分两半,那么句变成了

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

这个式子就是这个算法的精华所在,一定要把这个式子给理解了,

现在等于说预处理我们处理好了,然后我们就需要开始查询了,

查询其实也很简单的,你想一下,不管你给的任何区间,只要我们用两个dp表示就一定可以表示出来的

例如你要求   [l , r] 这个区间的最大值,

k=int(  (log2)  (r-l+1)  )

sum=max(dp[l][k] , dp[r-(1<<k)+1]  [r]   )

这就是查询了,

下面给一道题具体的练习一下吧,

poj3264

这道题就是典型的RMQ算法的例题,当然用线段树也可以解,不过代码量也有点复杂,

下面给出AC代码,

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
#define met(A,AA) memset(A,AA,sizeof(A))
const int maxn=50004;
int maxsum[maxn][20];
int minsum[maxn][20];
int n;
void B()
{
    for(int j=1;j<20;j++)
    {
        for(int i=1;i<=n;i++)
        {
            if((i+(1<<j)-1)>n) break;
            maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+(1<<j-1)][j-1]);
            minsum[i][j]=min(minsum[i][j-1],minsum[i+(1<<j-1)][j-1]);
        }
    }
}
int main()
{
    int t,r,l;
    met(maxsum,0);
    met(minsum,0);
    scanf("%d %d",&n,&t);

    for(int i=1;i<=n;i++)
    {
        scanf("%d",&maxsum[i][0]);
        minsum[i][0]=maxsum[i][0];
    }
	 B();
    while(t--)
    {
        scanf("%d %d",&l,&r);
        int k=int((log(r-l+1.0))/log(2.0));
        int MAX,MIN;
        MAX=max(maxsum[l][k],maxsum[r-(1<<k)+1][k]);
        MIN=min(minsum[l][k],minsum[r-(1<<k)+1][k]);
        printf("%d\n",MAX-MIN);
    }
    return 0;
}










 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值