RMQ

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RMQ相关的知识,希望对你有一定的参考价值。

解决RMQ(Range Minimum/Maximum Query)即区间最大最小值问题。

有一个离线算法(ST算法),这个算法是很高效了,时间是O(nlogn):(用O(nlogn)的时间进行预处理,再用O(1)的时间进行区间查询)

1.先是预处理(用动态规划解决)

 A数列为:3 2 4 5 6 8 1 2 9 7

F[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。同理 F[1,1] = max(3,2) = 3, F[1,2]=max(3,2,4,5) = 5,F[1,3] = max(3,2,4,5,6,8,1,2) = 8;

首先,DP的初始值,即第一次时,每次都是自己本身:F[i,0] = A[i]。

然后是动态转移方程 F[ i ][ j ] = max ( F[ i ] [ j-1 ], F[ i + 2^( j - 1 ) ] [ j - 1 ]);

代码:

 1 void RMQ(int num) //预处理->O(nlogn)  
 2 {
 3     //初始化
 4     for (int i = 1; i <= num; i++){
 5         minsum[i][0] = a[i];
 6         maxsum[i][0] = a[i];
 7     }
 8 
 9     for (int j = 1; j < 25; j++)
10     for (int i = 1; i <= num; i++)
11     if (i + (1 << j) - 1 <= num)
12     {
13         maxsum[i][j] = max(maxsum[i][j - 1], maxsum[i + (1 << (j - 1))][j - 1]);
14         minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
15     }
16 }

其中 i 和 j 的位置不能变,因为是先更新 1 个元素,再更新两个,四个.....等元素,以此类推更新所有长度的最值。

2.查询

 1 //查询
 2 int getmin(int x, int y){
 3     int k = (int)(log((double)(y - x + 1)) / log(2.0));
 4     return min(minsum[x][k], minsum[y - (1 << k) + 1][k]);
 5 }
 6 
 7 int getmax(int x, int y){
 8     int k = (int)(log((double)(y - x + 1)) / log(2.0));
 9     return max(maxsum[x][k], maxsum[y - (1 << k) + 1][k]);
10 }

对于需要查询的区间[ x, y],区间的差值为 len = y - x + 1;

那么查询的时候 设此时的 j 是 k,那么在查询时,需要 x -- x + 2^k -1 和 y - 2^k + 1 -- y之间,此时就要找到合适的K值。

1 int k=0;  
2     while((1<<(k+1))<=y-x+1)  
3         k++;  

用这个方法找也可以,不过有个更快捷的 k=log2( y - x + 1) 直接进行取值。

以上是关于RMQ的主要内容,如果未能解决你的问题,请参考以下文章

51nod1174(RMQ)

[luoguP1816] 忠诚(RMQ || 线段树)

Balanced Lineup(RMQ)

RMQ入门

RMQ ST表

AcWing 1270. 数列区间最大值(RMQ问题)