描述
給定一個長度不超過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;
}