【白话系列】倍增算法
我们就直接讲一下
R
M
Q
RMQ
RMQ(区间最值问题)问题。
题目:求出数列
a
a
a中下标在
l
−
r
l-r
l−r之间的数的最大值是多少?(当然也可以求最小值)
我们最简单的方法就是直接枚举 l − r l-r l−r之间的数。但当数据过于大时,暴力就不太适合了。
于是, S T ST ST算法应运而生。
思路
给定一个长度为 n n n的数组 a a a, S T ST ST算法可以能在 O ( N l o g N ) O(N\ log\ N) O(N log N)时间的预处理后,用 O ( 1 ) O(1) O(1)的时间复杂度在线回答 R M Q RMQ RMQ问题。
我们设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示子区间
i
i
i~
i
+
2
j
−
1
i+2^j-1
i+2j−1之间的最大值,也就是从
i
i
i开始的
2
j
2^j
2j个数的最大值。递推边界为
f
[
i
]
[
0
]
=
a
[
i
]
f[i][0]=a[i]
f[i][0]=a[i]。
递推式为:
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
预处理代码(求最大值)
void ST_perwork() {
for(int i=1;i<=n;i++) f[i][0]=a[i];
int t=log(n)/log(2)+1;
for(int j=1;j<t;j++) {
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
return ;
}
预处理代码(求最小值)
void ST_perwork() {
for(int i=1;i<=n;i++) f[i][0]=a[i];
int t=log(n)/log(2)+1;
for(int j=1;j<t;j++) {
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
return ;
}
询问 l − r l-r l−r最值代码(求最大值)
int ST_query(int l,int r) {
int k=log(r-l+1)/log(2);
return max(f[l][k],f[r-(1<<k)+1][k]);
}
询问 l − r l-r l−r最值代码(求最小值)
int ST_query(int l,int r) {
int k=log(r-l+1)/log(2);
return max(f[l][k],f[r-(1<<k)+1][k]);
}