描述
给定一个长度不超过10000的整数序列,对这个序列有不超过500000个询问,每次询问给定区间之内的最小值.
输入
第一行一个整数N(N<=10000)
第二行N个整数
第三行一个整数Q
以下共Q行,每行两个整数i,j用空格隔开,询问第i号元素到第j号元素之间的最小值
输出
每个询问输出一行,包含一个整数,为询问区间内的最小值
样例输入
5
1 2 3 4 5
2
1 5
3 4
样例输出
1
3
区间最值问题即RMQ的一个有效的解决方案是Sparse Table算法。算法思路是设f(i,j)表示从i开始长度为2^j的区间中的最值,以二维数组M[i][j]存储这个最值。设序列为A,则有M[i][0]=A[i]。将长度为2^j的区间平分为两段,则有f(i,j)=min(f(i,j-1),f(i+2^(j-1),j-1))。如f(2,1)=min(f(2,0),f(3,0))=min(A[2],A[3])。因此在时间复杂度O(n*logn)可以构建矩阵M。
构建矩阵后求出给定区间内的最值只需在M中寻找即可。假设要求i到j这个区间的最值,先设定一个k,k满足2^k<=(i-j+1)(这里我们取k=log(i-j+1)/log2)。这样就可以将[i,j]划分为两个(部分重叠)的长度为2^k的区间,最终结果就可以表示成f(i,j)=min(f(i,k),f(j-2^k+1,k))。
#include <iostream>
#include <math.h>
using namespace std;
#define min(a,b) a>b?b:a
int M[10001][14];
int A[10001];
void rmq(int N)
{
int i,j;
for(i=1;i<=N;++i)
M[i][0]=A[i];
for(j=1;j<=log(N+1.0)/log(2.0);++j)
{
for(i=1;i<=N-pow(2.0,(double)j)+1;++i)
M[i][j]=min(M[i][j-1], M[i+(int)pow(2.0,(double)j-1)][j-1]);//建立矩阵
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int N,i,j,Q,b;
scanf("%d",&N);
for(i=1;i<=N;++i)
{
scanf("%d",&A[i]);
}
rmq(N);
scanf("%d",&Q);
int k ;
while(Q--)
{
scanf("%d%d",&i,&j);
k = log((double)j-i+1)/log((double)2);//k的选择要求是要覆盖整个区间
b=min(M[i][k],M[j-(int)pow(2.0,(double)k)+1][k]);
printf("%d\n",b);
}
return 0;
}