RMQ
文章原创,转载请标明出处
(Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
ST算法(百度百科)
来看一下ST算法是怎么实现的(以最大值为例):
首先是预处理,用一个DP解决。设a是要求区间最值的数列,f[i,j]表示从第i个数起连续2^j个数中的最大值。例如数列3 2 4 5 6 8 1 2 9 7 ,f[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……从这里可以看出f[i,0]其实就等于a[i]。这样,DP的状态、初值都已经有了,剩下的就是状态转移方程。我们把f[i,j](j≥1)平均分成两段(因为j≥1时,f[i,j]一定是偶数个数字),从i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。用上例说明,当i=1,j=3时就是3,2,4,5 和6,8,1,2这两段。f就是这两段的最大值中的最大值。于是我们得到了动规方程
F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1])
。
接下来是得出最值,也许你想不到计算出f有什么用处,一般要想计算max还是要O(logn),甚至O(n)。但有一个很好的办法,做到了O(1)。还是分开来。如在上例中我们要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2,2]和f[5,2]得到。扩展到一般情况,就是把区间[l,r]分成两个长度为2^n的区间(保证有f对应)。直接给出表达式:
k:=trunc(ln(r-l+1)/ln(2));
ans:=max(F[l,k],F[r-2^k+1,k]);
这样就计算了从l开始,长度为2^k的区间和从r-2^k+1开始长度为2^k的区间的最大值(表达式比较繁琐,细节问题如加1减1需要仔细考虑),二者中的较大者就是整个区间[l,r]上的最大值。
自己编的模板题
/***RMQ-自己编的模板题***/
//by 智慧之神
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,a[1001],f[10001][15],k,l,r,res;
//n<=10000 2^14=16384
//f[i][j]中,i=n+1。j=以2为底i的对数
double temp;
int main()
{
scanf("%d%d",&n,&m);//n个数,m个询问
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);//读入n个数
f[i][0]=a[i];
//从i开始往后2的0次方个数,这里就是i了
}
temp=log(n)/log(2.0);
//以2为底,n的对数。为了确保范围不超n
for (int j=1;j<=temp;j++)//循环2的指数
{
for (int i=1;i<=n;i++)//循环第几个数
{
//f[i][j]表示:从i开始,向后2的j次方个数中的最大值
if (i+(1<<j)-1<=n)//如果没有超过n
{
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
// 图示如下:
// i\___________max__________/ i+2^(j)
// i\_____max____/i+2^(j-1)
// i+2^(j-1)\_____max____/ i+2^(j)
}
}
}
/*调试*/
// for (int i=1;i<=n;i++)
// {
// for (int j=0;j<=temp;j++)
// {
// printf("%d ",f[i][j]);
// }
// printf("\n");
// }
for (int i=1;i<=m;i++)//m条询问
{
scanf("%d%d",&l,&r);//读入左、右边界
k=log(r-l+1)/log(2);//确定2的指数范围
res=max(f[l][k],f[r-(1<<k)+1][k]);
// 图示如下:
// l\___________max__________/ r
// l\_____max______/l+2^k
// r-2^k\______max_____/ r
//即:[l,l+2^k] ∪ [r-2^k,r] = [l,r]
printf("%d\n",res);//输出结果
}
return 0;
}
/*
演示数据:
5 7
1 2 3 4 5
1 2
2 3
1 3
1 5
2 5
3 5
2 4
*/