求解区间最值RMQ学习笔记(在线ST算法)

好像是上学期学的算法了,趁着暑假再做最后一遍的复习,并写一篇博客。


有这样的一类问题,有一数列A,长度为n,有若干个查询q(一般来讲q<=1e5),每个查询会给出一个区间【x,y】,求解的是这个区间内的最值(最大值或者最小值)。

当然,线段树可以解决此类问题,查询logN的复杂度。

另一种方法便是在线ST(Sparse Table)算法,它可以在O(nlogn)的时间内对数据进行预处理,然后在O(1)回答所有的查询,以此来处理RMQ(Range Minimum/Maximum Query)问题。

所谓在线算法,就是每输入一个查询,就进行一次算法操作,并输出查询结果。离线算法,就是对所有的查询,用一个表结构存储起来,一次算法处理掉所有的查询,最后O(1)的复杂度输出。

在线算法通常需要进行数据的预处理,以便于后面的每个查询可以快速的查询到结果。

首先是预处理,这里需要用到动态规划来解决。

假设数列A为要查询的数列,dpi】【j】代表从第 i 个数开始连续 (2^j)个数中的最值(最大值/最小值),以下统一为求区间最大值

举个例子;

现在有数列:4 8 6 2 7 9 5 3 0,dp[1][0]=4,dp[1][1]=8(从第一个数开始,连续2^1个数的最大值,即子序列4 8的最大值),    dp[1][3]=9(子序列 4 8 6 2 7 9 5 3 的最大值),dp[2][0]=8,dp[2][1]=8......

从上面的例子我们可以看出,其实dp[i][0]=a[i],这样我们的dp数组的初值就可以设置了,即:dp[i][0]=a[i].

下面开始设置状态转移方程,在这里,我们把dp[i][j]这个区间分成两个部分,因为区间内的数的总数是连续个(2^j)个数字,这就代表一定是偶数个,第一部分为 【 i , 2^(j-1)-1】,第二部分为【 i+2^(j-1) , i+2^j -1 】,(因为我们数组设置的是从第 i 个数开始连续 (2^j)个数,所以我们在表示区间时,后面要减一),这样的话,我们可以看出,两个部分的区间长度都是2^(j-1),(做差即可得到)

这样我们就得到了状态转移方程

dp【i】【j】=max(dp【i】【j-1】,dp【i+(1<<(j-1))】【j-1】)

 

预处理代码:

void init()
{
    for(int i=1;i<=n;i++)
        dp[i][0]=a[i];
    for(int j=1;(1<

我们继续用上面的数列来说明一下上段话, 比如dp[1][3],代表的就是区间【1,4】,【5,8】这两个区间中的最大值中的最大值。

同样,我们继续把区间缩小或者扩大,仍然用这样的方法来表示。

于是我们就可以得到查询的方法了,假设我们要查询的区间为【x,y】,我们取delt为区间的长度(y-x+1)

令k=log2(delt).

那么我们要查询的区间【x,y】的最大值即为max(dp【x】【k】,dp【 y - (1<

 

比如我们要求【2,7】区间里的最大值,取按照上面的式子取k=2,则【2,7】就可以按照上面的方法划分为【2,5】,【4,7】两个部分,这两个部分的最大值即为dp[2][2]和dp[4][2]预处理后的值。

 

查询代码:

int query(int x,int y)
{
    double delt=y-x+1;
    int k=log(delt)/log(2.0);
    int ma=max(dp[x][k],dp[y-(1<

 

最后做道题热热身吧!

poj3264

 

 

你可能感兴趣的:(数据结构,动态规划/递推)