什么是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));
}
}