RMQ是 range maxm\imum(minimum) query 的缩写,是用来求某个区间内的最大值或者最小值,通常用在需要多次询问一些区间的最值问题中。
RMQ的原理是动态规划:
用A[1……N]表示一组数, F[I,J]表示从A[I]到A[I+2^j - 1]这个范围内的最大值,由于元素个数为2^J个,所以从中间平均分成两部分,每一部分的元素个数刚好为 2^(j-1)个。
整个区间的最大值一定是左右两部分最大值的较大值,满足动态规划的最优化原理
状态转移方程:
F[I,J] = max(F[I, J-1], F[I + 2^(J-1), J-1])
边界条件为F[I,0]= A[I];
这样就可以在O(NlgN)的时间复杂度内预处理F数组。
预处理代码:
for(int i = 1 ; i <= mn; 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]);
对于询问区间[L,R],求出最大的x,满足2^x<= R-L+1,即 x = trunc(ln(R-L+1) / ln(2)),两个子区间元素个数都是2^x个,
ans= max(F[L,x],F[R+1-2^x,x]);
询问操作代码:
long query(int L, int R)
{
int x = int(ln(R-L+1) / ln(2));
return max( f[L][x], f[R-(1<<x)+1][x]);
}
#include <iostream> //区间问题
#include <cmath>
#include <cstdio>
using namespace std;
int RMQ[100000][23], n, m;
int ASK(int L, int R)
{
int x = (int)(log(R-L+1) / log(2));
return max(RMQ[L][x], RMQ[R-(1<<x)+1][x]);
}
void ST()
{
int i , j;
for(i = 1; i <= n; i++)
cin>> RMQ[i][0];
for(j = 1; j <= (int)(log(n)/log(2)); j++)
for( i = 1; i+(1<<j)-1 <= n; i++)
RMQ[i][j] = max(RMQ[i][j-1], RMQ[i+(1<<j-1)][j-1]);
}
int main()
{
int i, x, y;
cin >> n >> m;
ST();
for( i = 1; i <= m; i++)
{
scanf("%d%d", &x,&y);
printf("%d\n", ASK(x,y));
}
return 0;
}