RMQ问题:区间最小值问题(也可以解决区间最大值问题)
ST算法分为预处理和查询两部分
首先定义数组:我们用定义 Amax[i][j] 为从 i开始的,长度为2^j的区间里面的最大值, Amin[i][j]为从i开始,长度为2^j的区间里面的最小值
实际问题
给出一个N个数的序列A[] 和M次查询,查询如下:MAX x y 求区间[x, y]的最大值,MIN x y 求区间[x, y]的最小值。
代码:
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define MAXN 10010 #define INF 1000000000 using namespace std; int A[MAXN]; int N, M; int Amax[MAXN][50];//Amax[i][j]表示从i开始的,长度为2的j次方的区间里面的最大值 int Amin[MAXN][50];//Amin[i][j]表示从i开始的,长度为2的j次方的区间里面的最小值 void RMQ_init() { for(int i = 1; i <= N; i++) Amax[i][0] = Amin[i][0] = A[i]; for(int j = 1; (1<<j) <= N; j++) { for(int i = 1; i + (1<<j)-1 <= N; i++) { Amax[i][j] = max(Amax[i][j-1], Amax[i+(1<<(j-1))][j-1]); Amin[i][j] = min(Amin[i][j-1], Amin[i+(1<<(j-1))][j-1]); } } } int query(char *op, int L, int R) { //k值求法有两种 int k = 0; while((1<<(k+1)) <= R-L+1) k++; //int k = (int)(log(double(R - L + 1))/log((double)2)); if(strcmp(op, "MAX") == 0) return max(Amax[L][k], Amax[R-(1<<k)+1][k]); else return min(Amin[L][k], Amin[R-(1<<k)+1][k]); } int main() { while(scanf("%d%d", &N, &M), N||M) { for(int i = 1; i <= N; i++) scanf("%d", &A[i]); RMQ_init(); char op[10]; int x, y; while(M--) { scanf("%s%d%d", op, &x, &y); printf("%d\n", query(op, x, y)); } } return 0; }
上面说过了RMQ的使用——求序列任意区间长度的最大值以及最小值。
延伸:给出一个N个数的序列,并固定区间长度为k。M次查询 MAX x (MIN x) ——查询[x,x+k-1]的最大值(最小值)。
这时我们同样可以用上面的方法做。但这样会浪费很大的内存,因为对于这道题,我们只需要使用当区间长度为k时的信息(区间长度不为k的信息我们都用不到)。
思考一下?怎么优化?只需要使用当区间长度为k时的信息!
也就是说我们没有必要用二维数组来存储所有区间长度的可能取值。
我们只需要这样定义:Amax[ i ] 存储的是以序列A[ i ]开始的,长度为k的区间里面的最大值。Amin[ i ]同样如此。
这样的话,我们只需要按长度递增来推出区间长度为k时的情况 就ok了。
关于查询,也没什么变动,设置一个变量len。找到合适的len —— 使得以L开始的和以R结尾的两个长度为2^len的区间覆盖查询区间即可。
详看代码:
#include <cstdio> #include <cstring> #include <algorithm> #define MAXN 1000000+10 using namespace std; int N, M, k; int A[MAXN]; int Amax[MAXN]; int Amin[MAXN]; void RMQ_init() { for(int i = 1; i <= N; i++) Amax[i] = Amin[i] = A[i]; for(int j = 1; (1<<j) <= k; j++) { for(int i = 1; i + (1<<j) - 1 <= N; i++) { Amax[i] = max(Amax[i], Amax[i + (1<<(j-1))]);//压缩 Amin[i] = min(Amin[i], Amin[i + (1<<(j-1))]); } } } int query(char *op, int L, int R) { int len = 0; while(1<<(len+1) <= R-L+1) len++;//求len值 if(strcmp(op, "MAX") == 0) return max(Amax[L], Amax[R-(1<<len)+1]); else return min(Amin[L], Amin[R-(1<<len)+1]); } int main() { while(scanf("%d%d%d", &N, &M, &k) != EOF) { for(int i = 1; i <= N; i++) scanf("%d", &A[i]); RMQ_init(); char op[10]; int x; while(M--) { scanf("%s%d", op, &x); printf("%d\n", query(op, x, x+k-1)); } } return 0; }