RMQ问题:区间最小值问题(也可以解决区间最大值问题)
解决算法:Sparse - Table算法,简称ST,基于动态规划求区间最值的算法
ST算法分为预处理和查询两部分
首先定义数组:我们用定义 maxx[i][j] 为从 i开始的,长度为2^j的区间里面的最大值,
Amin[i][j]为从i开始,长度为2^j的区间里面的最小值
预处理
首先我们对于区间的预处理是将长度为2^j的区间分成两个长度为2^(j-1)的区间【就像二分那样】
然后得到诸如 i……i+2^(j-1)-1 和 i+2^(j-1)……i+2^j-1 的区间
便能得到转移方程【顺序与j正相关】
maxx[i][j]=max(maxx[i][j-1],maxx[i+2^(j-1)][j-1])
minn[i][j]=min(minn[i][j-1],minn[i+2^(j-1)][j-1])
查询方程
MAX(L,R)表示查询区间[L,R]内的最大值;
MIN(L,R)表示查询区间[L,R]内的最小值。
我们有必要寻找一个合适的最小的k使得 以L开始的长度为2^k的区间 与 以R结束的长度为2^k的区间 刚好能够覆盖整个[L,R]区间。
可以如此计算k值:从0递增直到2^(k+1)>R-L+1
code:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 10010
#define inf 0x3f3f3f3f
int a[maxn],n,m,maxx[maxn][50],minn[maxn][50]; //即文中所表意思
void RMQ_in() {
for(int i = 1; i <= n; i++)
maxx[i][0] = minn[i][0] = a[i];
for(int j = 1; (1<<j) <= n; j++) {
for(int i = 1; i+(i<<j)-1 <= n; i++) {
maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);
minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
}
}
}
int query(int *op,int l,int r) { //判断求最大或最小
//k的一种解法
int k=0;
for(; (1<<(k+1)) < (r-l+1); k++);
if(strcmp(op, "MAX") == 0)
return max(maxx[L][k],maxx[R-(1<<k)+1][k]);
else
return min(minn[L][k],minn[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_in();
char op[10];
int x,y;
for(int i = 1; i <= m; i++) {
scanf("%s%d%d",op,&x,&y);
printf("%d\n",query(op,x,y));
}
}
return 0;
}
但是各位刷题大佬们肯定有所发现,有些RMQ类型的题目是制定询问区间长度为k的,此时如果还想像上面一样解答,怕是会浪费极大的空间,所以优化是必不可少的。↓↓↓↓
↓↓↓↓↓↓
优化
我们只需要长度为k的代码,这就是说,我们并不需要使用二维数组来储存,而是只使用一维数组,
那么只需要定义一维数组 maxx[i] 来表示以i为起始,长度近似为k的区间的最大值就行了
这样的话,我们只需要每次长度递增到k就行了。
当然对于查询的话只需要提取就可以了。
code:
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#define maxn 10010
using namespace std;
int a[maxn],n,m,k,t,maxx[maxn],minn[maxn];
void RMQ_in() {
for(int i=1; i <= n; i++)
maxx[i]=minn[i]=a[i];
for(int j=1; (1<<j) <= k; j++) {
for(int i=1; i+(1<<j)-1 <= n; i++) {
maxx[i]=max(maxx[i],maxx[i+(1<<(j-1))]);
minn[i]=min(minn[i],minn[i+(1<<(j-1))]);
}
}
}
int query(char *op,int l,int r,int t) {
if(strcmp(op,MAX) == 0)
return max(maxx[l],maxx[r-(1<<t)]);
else
return min(minn[l],minn[r-(1<<t)]);
}
int main() {
while(scanf("%d%d%d",&n,&m,&t) != EOF) {
char op[10];
for(t=0; (1<<(t+1)) <= k; t++);
for(int i=1; i <= n; i++) scanf("%d",&a[i]);
for(int i=1; i <= m; i++) {
int x;
scanf("%s%d",op,&x);
printf("%d\n",query(op,x,x+k-1,t));
}
}return 0;
}