RMQ(Range Minimum/Maximum Query) 对于长度为n的数列A,回答若干询问RMQ(A, i, j) (i,j<=n),返回数列A中下标在i,j里的最小(大)值
给出m次查询 最常想到的是遍历 复杂度为O(nm).
改进方法:
(1)、ST(sparse table)算法
基于这样的一个事实:一大块区间求最大/小值,可以分成两个小块分别求出最大/小值(分治法的思想),然后再将 求出的两个极值 比较 ,求出最后最值。
例如:
a)、预处理
分配一个d[n][log2n]大小的数据区:
d[i][k] 记录数列A[i, i+2k-1]之间的最值(包括两端点),时间复杂度O(nlog2n)
在做预处理时有一个动态规划的思想:d[i][k] = min/max( d[i][ k-1 ],d[ i+2k-1 ][ k-1 ] ).
b)、处理查询
k=[log2(j-i+1)];
RMQ(i,j) = min/max(d[i][k],d[j-2k+1][k]);
这里有个小技巧是: d[j-2k+1][k] 从下标j开始往前数2k个数 与 d[i][k]从下标i开始往后数2k个数一定包括了A[i,j]这个区间(个数小于2k+1)
时间复杂度O(1)
#include <iostream> #include <stdlib.h> #include <math.h> using namespace std; void swap(int *i,int *j); int query(int i,int j); int **table; void RMQ(int arr[],int n) { //cout<<"RMQ"<<endl; int w = log2(n); for(int i=1;i<=n;i++)table[i][0]=arr[i];//cout<<table[i][0]<<endl; int tmp;//cout<<w<<endl; for(int i=1;i<=w;i++) { tmp=1;tmp=tmp<<(i-1); for(int j=1;j+tmp<=n;j++) { if(i==4&&j==1)cout<<*(*(table+j)+i-1)<<" "<<*(*(table+j+tmp)+i-1)<<endl; if(*(*(table+j)+i-1) < *(*(table+j+tmp)+i-1)) *(*(table+j)+i) = *(*(table+j)+i-1); else *(*(table+j)+i) = *(*(table+j+tmp)+i-1); } } } int main() { int n; cin>>n; int *arr = (int *)malloc(sizeof(int)*(n+1)); int *p = arr; for(int i=1;i<=n;i++) { *(p+i) = rand()%17 + 1; cout<<*(p+i)<<" "; }cout<<endl; table = (int **)malloc(sizeof(int*)*(n+1)); for(int i=1;i<=n;i++)*(table+i) = (int *)malloc(sizeof(int)*(log2(n)+1)); RMQ(arr,n); for(int j=1,k=1;j<=n;j++) { int step = 0;cout<<j<<": "; for(int i=0;j+(1<<i)-1<=n;i++) { printf("%5d ",*(*(table+j)+i)); } cout<<endl; } int m; cin>>m; while(m--) { int i,j; cin>>i>>j; if(i>j)swap(i,j); if(i<1)i=1; if(j>n)j=n; cout<<"result: "<<query(i,j)<<endl; } for(int i=1;i<=100;i++) { cout<<*(p+i)<<" "<<endl; } system("pause"); return 0; } void swap(int *i,int *j) { int tmp = *i; *i = *j; *j = tmp; } int query(int i,int j) { int dif = j-i+1; int log_dif = log2(dif); int tmp = 1; //cout<<i<<" "<<j<<" "<<log_dif<<endl; if(*(*(table+i)+log_dif) < *(*(table+j-(tmp<<log_dif)+1)+log_dif)) return *(*(table+i)+log_dif); else return *(*(table+j-(tmp<<log_dif)+1)+log_dif); }