RMQ - 求区间最值
RMQ
RMQ算法,是一个快速求区间最值的离线算法,预处理时间复杂度O(n*log(n)),查询O(1)。
概念:RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。
预处理
设A[i]是要求区间最值的数列,F[i, j]表示从第i个数起连续2j个数中的最大值。(DP的状态)
例如:A数列为3 2 4 5 6 8 1 2 9 7
首先初始化F[i,0]=A[i],F[1,0]代表从第一个数,长度为20的最大值,即为3。同理F[1,1]=max{3,2}=3,F[1,2]=max{3, 2, 4, 5}=5。
推导状态转移方程
将F[i,j]平均分成两份(F[i,j]一定是偶数),从i到i+2j-1-1为一段,i+2j-1到i+2j-1为一段。(长度都为2j-1)
例如:当i=1,j=3时就是3,2,4,5和6,8,1,2,F[i,j]就是这两段中各自的最大值。
由此状态转移方程为:F[i,j]=max{F[i,j-1],F[i+2j-1,j-1]}。
代码如下:
void RMQ(int num)//num是数量
{
for(int i=1; i<=num; i++)
{
dp_min[i][0]=arr[i];
dp_max[i][0]=arr[i];
}
for(int j=1; (1<<j)<=num; j++)
for(int i=1; i+(1<<j)-1<=num; i++)
{
dp_max[i][j]=max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
dp_min[i][j]=min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
}
}
void query(int l,int r,int op) //op=1 查找最大值,op=0,查找最小值
{
int k=log2(r-l+1);
if(op)
return max(dp_max[l][k],dp_max[r-(1<<k)+1][k]);
else
return min(dp_min[l][k],dp_min[r-(1<<k)+1][k]);
}