欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

提高篇(1):RMQ问题与ST表

程序员文章站 2022-06-20 14:45:29
RMQ是英文Range Minimum/Maximum Query的缩写,是询问某个区间内的最值,这里讲一种解法:ST算法 ST算法通常用在要多次(10^6级别)询问区间最值的问题中,相比于线段树,它实现更简单,效率更高,但不支持修改,且一般只能维护最值。 ST算法实际上是动规,原理如下: 预处理: ......

RMQ是英文Range Minimum/Maximum Query的缩写,是询问某个区间内的最值,这里讲一种解法:ST算法

ST算法通常用在要多次(10^6级别)询问区间最值的问题中,相比于线段树,它实现更简单,效率更高,但不支持修改,且一般只能维护最值。

ST算法实际上是动规,原理如下:

预处理:

一组数a[1]..a[n],设f[i][j]表示从a[i]到a[i+2^j-1]这个范围中的最值,元素个数为2^j个。

可以分成2部分,即从a[i]至a[i+2^(j-1)-1]与a[i+2^(j-1)]至a[i+2^j-1],所以

f[i][j]也可以分成f[i][j-1]与f[i+2^(j-1)][j-1],整个区间的最大值一定是左右两部分最大值的较大值,

于是可得状态转移方程:f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1]),边界条件为f[i][0]=a[i],这样即可在O(n log(n))的时间内预处理f数组。

询问:

若询问区间[l,r]的最大值,可以先求出最大的x,满足2^x<=r-l+1,那么区间[l,r]=[l,l+2^x-1]U[r-2^x+1,r],两个区间的元素个数都为2^x,

所以[l,r]中的最大值为max(f[l][x],f[r-2^x+1][x]),可以在O(1)内计算出来(对于m次询问,需要O(m)的时间复杂度)。这两个区间虽然有交集,但对最值没有影响,这就是ST算法只使用于区间最值的原因。

总结:

求区间[x,y]的最大值:

k=log2(y-x+1);

ans=max(f[x][k],f[y-2^k+1][k]);

技巧:

因为cmath库中的log2函数效率不高,所以为了提高速度,通常会使用O(N)的递推预处理出1~N这N种区间长度各自对应的k值。

具体地,设log[x]表示log2(x)向下取整,则log[x]=log[x/2]+1。这样总时间复杂度为log(n*log(n)+m+n)。

放一道例题:

平衡阵容(Balanced Lineup)

题目描述

天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. 有一天, John决定让一些牛们玩一场飞盘比赛. 他准备找一群在对列中为置连续的牛来进行比赛. 但是为了避免水平悬殊,牛的身高不应该相差太大. John 准备了Q (1 <= Q <= 180,000) 个可能的牛的选择和所有牛的身高 (1 <= 身高 <= 1,000,000). 他想知道每一组里面最高和最低的牛的身高差别. 注意: 在最大数据上, 输入和输出将占用大部分运行时间. 

输入

6 3
1
7
3
4
2
5
1 5
4 6
2 2

输出

6

3

0

[参考程序]

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<climits>
 5 #include<cmath>
 6 #include<algorithm>
 7 using namespace std;
 8     
 9 const int N = 50005;
10 int FMAX[N][20], FMIN[N][20];
11     
12 void RMQ(int n)
13 {
14     for(int j = 1; j != 20; ++j)
15     {
16         for(int i = 1; i <= n; ++i)
17         {
18             if(i + (1 << j) - 1 <= n)
19             {
20                 FMAX[i][j] = max(FMAX[i][j - 1], FMAX[i + (1 << (j - 1))][j - 1]);
21                 FMIN[i][j] = min(FMIN[i][j - 1], FMIN[i + (1 << (j - 1))][j - 1]);
22             }
23         }
24     }
25 }
26     
27 int main()
28 {
29     int num, query;
30     int a, b;
31     while(scanf("%d %d", &num, &query) != EOF)
32     {
33         for(int i = 1; i <= num; ++i)
34         {
35             scanf("%d", &FMAX[i][0]);
36             FMIN[i][0] = FMAX[i][0];
37         }
38         RMQ(num);
39         while(query--)
40         {
41             scanf("%d%d", &a, &b);
42             int k = (int)(log(b - a + 1.0) / log(2.0));
43             int maxsum = max(FMAX[a][k], FMAX[b - (1 << k) + 1][k]);
44             int minsum = min(FMIN[a][k], FMIN[b - (1 << k) + 1][k]);
45             printf("%d\n", maxsum - minsum);
46         }
47     }
48     return 0;
49 }

参考书籍:《信息学奥赛一本通·提高篇》