RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
用F[i,j]表示从第i个数开始连续2^j个数的最值!!!(注意不是到第2^j个数)
那么,可知每个区间有偶数个数(除了2^0),将这些区间分成两个区间,i到2^(j-1)-1,和i+2^(j-1)到2^j-1,所以可得动态方程为F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1]);
初始状态为F[i,0],即,从第i个数开始连续1个数的最值,也就是第i个数本身。
void RMQ(int num){
for(int j=1;j<32;j++){//int类型最大2^32-1
for(int i=1;i<=num;i++){
if(i+(1<<j)-1<=num){
maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+(1<<(j-1))][j-1]);
minsum[i][j]=max(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]);
}
}
}
}
这里我们需要注意的是循环的顺序,我们发现外层是j,内层所i,这是为什么呢?可以是i在外,j在内吗?
答案是不可以。因为我们需要理解这个状态转移方程的意义。
状态转移方程的含义是:先更新所有长度为F[i,0]即1个元素,然后通过2个1个元素的最值,获得所有长度为F[i,1]即2个元素的最值,然后再通过2个2个元素的最值,获得所有长度为F[i,2]即4个元素的最值,以此类推更新所有长度的最值。
而如果是i在外,j在内的话,我们更新的顺序就是F[1,0],F[1,1],F[1,2],F[1,3],表示更新从1开始1个元素,2个元素,4个元素,8个元素(A[0],A[1],....A[7])的最值,这里F[1,3] = max(max(A[0],A[1],A[2],A[3]),max(A[4],A[5],A[6],A[7]))的值,但是我们根本没有计算max(A[0],A[1],A[2],A[3])和max(A[4],A[5],A[6],A[7]),所以这样的方法肯定是错误的。
假设查询的区间是(i,j),那么int k=log2(i-j+1)。取最小幂。
举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2]);
例题(hdu 5443 The water problem):
/*
* mai.cpp
*
* Created on: 2015年9月15日
* Author: chen
*/
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<vector>
#include<time.h>
#include<queue>
#include<stack>
#include<iterator>
#include<math.h>
#include<stdlib.h>
#include<limits.h>
#include<memory.h>
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff
#define FOR(i,a) for((i)=0;i<(a);(i)++) //[i,a);
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c)scanf("%d%d%d",&a,&b,&c)
#define for1(i,a,b) for(int i=(a);i<b;i++)
#define for2(i,a,b) for(int i=(a);i<=b;i++)
#define for3(i,a,b)for(int i=(b);i>=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
#define LL __int64
const double PI = acos(-1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
template<class T> inline T Min(T a, T b) { return a < b ? a : b; }
template<class T> inline T Max(T a, T b) { return a > b ? a : b; }
using namespace std;
template<class T>
T Mint(T a, T b, T c) {
if (a>b) {
if (c>b)
return b;
return c;
}
if (c>a)
return a;
return c;
}
template<class T>
T Maxt(T a, T b, T c) {
if (a>b) {
if (c>a)
return c;
return a;
}
else if (c > b)
return c;
return b;
}
#define maxn 50
int T,n,q,l,r;
int maxsum[1010][maxn];
int minsum[1010][maxn];
void RMQ(int num){
for(int j=1;j<20;j++){
for(int i=1;i<=num;i++){
if(i+(1<<j)-1<=num){
maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+(1<<(j-1))][j-1]);
//minsum[i][j]=max(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]);
}
}
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
while(~sf(T)){
while(T--){
sf(n);
for2(i,1,n){
sf(maxsum[i][0]);
}
RMQ(n);
sf(q);
while(q--){
sfd(l,r);
int k=(int)(log(r-l+1.0)/log(2.0));
int ans = max(maxsum[l][k], maxsum[r - (1<<k) + 1][k]);
pf(ans);
}
}
}
return 0;
}