RMQ(Range Maximum(Minimum) Query)问题,查询区间最大值或最小值,相比线段树,为静态查询,复杂度 O ( n log n ) O(n\log n) O(nlogn)
更好的阅读体验:我的博客
作用
查询区间最大值或最小值
复杂度
O ( n log n ) O(n\log n) O(nlogn) 预处理, O ( 1 ) O(1) O(1) 查询
算法:倍增思想
以最大值为例,最小值取min
同理
预处理
Max[i][j]
表示从 i i i 位置开始的 2 j 2^j 2j 个数中的最大值(最小值同理),即表示区间 [ i , i + 2 j ) [ i , i+2^j ) [i,i+2j) 的最大值- 将 [ i , i + 2 j ) [ i , i+2^j ) [i,i+2j) 区间拆成 [ i , i + 2 j − 1 ) [ i,i+2^{j-1} ) [i,i+2j−1) 和 [ i + 2 j − 1 , i + 2 j ) [i+2^{j-1},i+2^j ) [i+2j−1,i+2j) 两个区间,则大区间的最大值是小区间最大值的最大值
- 更新方程:
Max[i][j]=max(Max[i][j-1] , Max[i+2^{j-1}][j-1])
查询
-
查询 [ l , r ] [ l , r ] [l,r] 区间最大值:取区间长度的对数 k = log 2 ( r − l + 1 ) k=\log_{2} (r-l+1) k=log2(r−l+1),查询结果为
max(Max[l][k], Max[r-2^k+1][k])
-
上述第二项 r − 2 k + 1 r-2^k+1 r−2k+1 的由来:尝试找右短点为 r r r 的区间。设左端点为 x x x ,则 x + 2 k = r + 1 x+2^k =r+1 x+2k=r+1(左闭右开区间),解得左端点 x = r − 2 k + 1 x=r-2^k+1 x=r−2k+1
模板
void init() // 初始化,st[i][0]中记录了数组的初始值
{
for(int j=1;j<20;j++) // 区间长度不超过(1<<19)(524288,够用了)
for(int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]); // 更新最大值
}
int RMQ(int l,int r) // 查询区间[l,r]的最大值
{
int k=log2(r-l+1); // 求左端点(算法中细说了)
return max(st[l][k],st[r-(1<<k)+1][k]); // 得到最大值
}
int main() // 普通RMQ问题主函数
{
read(n),read(m);
for(int i=1;i<=n;i++)
read(st[i][0]); // 原始值写入st[i][0]
init(); // 初始化
int l,r;
for(int i=0;i<m;i++)
{
read(l),read(r); // 查询
printf("%d\n",RMQ(l,r));
}
return 0;
}
二维RMQ问题
目标
查询 a × b a\times b a×b 矩阵中 n × n n\times n n×n 大小的矩阵中的最大值
修改一维ST表
Max[i][j][k]
表示 ( i , j ) (i,j) (i,j) 为左上角,边长为 2 k 2^k 2k 的正方形矩阵中的最大值- 递推:
Max[i][j][k]=max(Max[i][j][k-1], Max[i][j+2^{k-1}][k-1], Max[i+2^{k-1}][j][k-1], Max[i+2^{k-1}][j+2^{k-1}][k-1])
,即将大正方形拆成上下左右四个小正方形 - 查询:
(
x
,
y
)
(x,y)
(x,y) 为左上角,
n
n
n 为边长,
k
=
log
2
n
k=\log_{2} n
k=log2n,结果为
max(Max[x][y][k], Max[x][y+n-2^k+1][k], Max[x+n-2^k+1][y][k], Max[x+n-2^k+1][y+n-2^k+1][k]
,区间端点的推到与一维类似
void init()
{
for(int k=1;k<=(int)log2(min(a,b))+1;k++)
for(int i=1;i+(1<<k)-1<=a;i++)
for(int j=1;j+(1<<k)-1<=b;j++)
{
stmax[i][j][k]=max(stmax[i][j][k-1], max(stmax[i][j+(1<<(k-1))][k-1],
max(stmax[i+(1<<(k-1))][j][k-1], stmax[i+(1<<(k-1))][j+(1<<(k-1))][k-1])));
stmin[i][j][k]=min(stmin[i][j][k-1], min(stmin[i][j+(1<<(k-1))][k-1],
min(stmin[i+(1<<(k-1))][j][k-1], stmin[i+(1<<(k-1))][j+(1<<(k-1))][k-1])));
}
}
int RMQ(int x,int y)
{
int k=log2(n+1);
int maxn=max(stmax[x][y][k],max(stmax[x][y+n-(1<<k)][k],
max(stmax[x+n-(1<<k)][y][k],stmax[x+n-(1<<k)][y+n-(1<<k)][k])));
int minn=min(stmin[x][y][k],min(stmin[x][y+n-(1<<k)][k],
min(stmin[x+n-(1<<k)][y][k],stmin[x+n-(1<<k)][y+n-(1<<k)][k])));
return maxn-minn;
}
int main()
{
read(a),read(b),read(n);
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
{
read(stmax[i][j][0]);
stmin[i][j][0]=stmax[i][j][0];
}
init();
int ans=inf;
for(int i=1;i<=a-n+1;i++)
for(int j=1;j<=b-n+1;j++)
ans=min(ans,RMQ(i,j));
printf("%d",ans);
return 0;
}
题目
Uva1618
洛谷P2216 二维RMQ
洛谷P1816
洛谷P2880