RMQ算法

什么是RMQ?

RMQ指的是求一个区间的某个数值(例如:最大值,最小值…)

首先还是要先推数学公式

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

例如: a数列为:3 2 4 5 6 8 1 2 9 7

F[1,0]表示第1个数起,长度为2^0=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;

并且我们可以容易的看出F[i,0]就等于a[i]。(DP的初始值)

我们把F[i,j]平均分成两段(因为F[i,j]一定是偶数个数字)
从 i 到i + 2(j-1) - 1为一段,i + 2 (j-1)到i + 2 j - 1为一段
(长度都为2 (j-1))。

于是我们得到了状态转移方程F[i, j]=max(F[i,j-1] , F[i+2^(j-1),j-1])

这里你可能觉得有问题,但其实是这样的 i +2(j-1)+2(j-1)=i + 2j = F ( i , j )

最最重要的来了!我们要怎样查询呢,我们上面讲了是以2为倍数查的,但是数组的个数不一定是2的倍数啊,那怎么办呢?

我们把他分成两个2的倍数,如a[5]={1,2,3,4,5},我们就比较1 2 3 4和2 3 4 5两个区间的最大值取最大值即可

void work(int n) 
{
    for (int i=1;i<=n;i++) f[i][0]=a[i];
    for (int j=1;(1<<j)<=n;j++) 
        for ( int i=1;i+(1<<j)-1<=n;i++) 
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j - 1]);
}
int RMQ(int l, int r) 
{
    re int k=0;
    while ((1<<(k+1))<=r-l+1)
         k++;
    return max(f[l][k],f[r-(1<<k)+1][k]);
}

上题目!
P2880 [USACO07JAN]Balanced Lineup G

#include<cstdio>
#include<iostream>
#define re register
using namespace std;
int a[2000005],f[50005][20],dp[50005][20];
void work(int n) 
{
    for (int i=1;i<=n;i++)
	{
    	f[i][0]=a[i];
	}
    for (int j=1;(1<<j)<=n;j++)
	{
    	for (int i=1;i+(1<<j)-1<=n;i++)
		{
    		f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j - 1]);
		}
	} 
}
int RMQ(int l, int r) 
{
    int k=0;
    while ((1<<(k+1))<=r-l+1)
	{
    	k++;
	}       
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
void work2(int n) 
{
    for (int i=1;i<=n;i++)
	{
    	dp[i][0]=a[i];
	}
    for (int j=1;(1<<j)<=n;j++)
	{
    	for (int i=1;i+(1<<j)-1<=n;i++)
		{
    		dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j - 1]);
		}
	} 
}
int RMQ2(int l, int r) 
{
    int k=0;
    while ((1<<(k+1))<=r-l+1)
	{
        k++;	
	}        
    return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++)
	{
    	scanf("%d",&a[i]);
	}       
    work(n);    
    work2(n);
    for (int i=1;i<=m;i++)
    {
    	int l,r;
    	cin>>l>>r;
    	printf("%d\n",RMQ(l,r)-RMQ2(l,r));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值