RMQ 问题之Sparse_Table算法

RMQ问题,全名(Range Minimum/Maximum Query),是求给定区间中的最值问题。

主要方法及复杂度如下:

1、朴素(即搜索),O(n)-O(qn) online。

2、线段树,O(n)-O(qlogn) online。

3、Sparse_Table(实质是动态规划),O(nlogn)-O(1) online。

4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(1) online。

昨天刚刚学了第三种,ST算法。

ST算法可以在O(nlogn)的预处理以后实现O(1)的查询效率,从而解决查询次数很多(如大于100万)的RMQ问题。

首先,是预处理。预处理是采用dp的思想,我们用f[i][j]表示区间[i,i+2^j-1]中的最大值(即从i开始,长度为2^j的闭区间)。

开始时,f[i][0]一定等于num[i]。好了,初始值找到了,下面是状态转移方程:

f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])。即把[i,i+2^j-1]区间分成两部分[i,i+2^(j-1)-1]和[i+2^(j-1),i+2^(j-1)+2^(j-1)-1],正好和原区间一致。

有了初始值和转移方程,我们可以自底向上递推出所有的f[i][j]的值。

对于边界,还要注意一点。由于区间长度最大为n,所以二维边界最大为log(n)/log(2.0);

一维边界只要满足对于每个起始点,都可以有长度找到n就行了,也就是让i+2^j-1<=n就好了。

然后就是查询了。假设要查询区间[a,b]的最大值,由于区间的长度很可能不是2的整数幂,所以我们要把区间划分为长度为2的整数幂的两部分,而且这两个区间的并集必须是[a,b]。为了实现这个方案,我们需要先求出一个最大的k,使得2^k<=(b-a+1),这样就可以把区间分成两部分[a,a+2^k-1]和[b-2^k+1,b],使他们既能不超过a,b区间的范围,又能把区间全部覆盖。于是,[a,b]区间的最大值就等于上述两个区间的最大值中最大的那个。

//其他解释

算法(SparseTable):

       它是一种动态规划的方法。以最小值为例。a为所寻找的数组,用一个二维数组   f(i,j)记录区间[i,i+2^j-1]区间中的最小值。其中f[i,0] = a[i];所以,对于任意 的一组(i,j),f(i,j) =min{f(i,j-1),f(i+2^(j-1),j-1)}来使用动态规划计算出来。   这个算法的高明之处不是在于这个动态规划的建立,而是它的查询:它的查询效 率是O(1)!如果不细想的话,怎么弄也是不会想到有O(1)的算法的。假设我们要求区间[m,n]中的最小值,找到一个数k使得2^k<n-m+1,即k=[ln(b-a+1)/ln(2)]这样, 可以把这个区间分成两个部分:[m,m+2^k-1]和[n-2^k+1,n]!我们发现,这两个区间是已经初始化好的!前面的区间是f(m,k),后面的区间是f(n-2^k+1,k)!这样,只要 看这两个区间的最小值,就可以知道整个区间的最小值!

小结:
        稀疏表(SparseTable)算法是O(nlogn)-O(1)的,对于查询很多大的情况下比较好。
        ST算法预处理:用dp[i,j]表示从i开始的,长度为2^j 的区间的RMQ,则有递推式 dp[i,j]=min{dp[i,j-1],dp[i+2j-1,j-1]},即用两个相邻的长度为2j-1的块,更新长度为2j的块。

    因此,预处理时间复杂度为O(nlogn)。这个算法记录了所有长度形如2k的所有询问的结果。从这里可以看出,稀疏表算法的空间复杂度为O(nlogn)。

代码:

#include <math.h>
#include <stdio.h>
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
const int N=100005;
int n,Q,c[N],a,b;
int dp_max[N][20];  //20不一定是唯一的。需要计算log(N)/log(2)
int dp_min[N][20];  
void Init()
{
    for(int i=1;i<=n;i++)
        dp_max[i][0] = dp_min[i][0] = c[i];
    double limit = log(n)/log(2.0);
    for(int j=1;j<=(int)limit;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            dp_max[i][j] = max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
            dp_min[i][j] = min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
        }
}
int Get_Max(int a,int b)
{
    int k = (int)(log(b-a+1)/log(2.0));
    return max(dp_max[a][k],dp_max[b-(1<<k)+1][k]);
}
int Get_Min(int a,int b)
{
    int k = (int)(log(b-a+1)/log(2.0));
    return min(dp_min[a][k],dp_min[b-(1<<k)+1][k]);
}
int main()
{
    scanf("%d%d",&n,&Q);
    for(int i=1;i<=n;i++)
        scanf("%d",&c[i]);
    Init();
    while(Q--)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",Get_Max(a,b)-Get_Min(a,b));
    }
    return 0;
}

学习自强哥博客:
RMQ问题之Sparse_Table算法

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值