RMQ(Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j),返回数列A中下标i,j里的最小/大值,即RMQ问题是指求区间最值的问题。

时间复杂度:O(N)~ O(logN)

主要思想:分治/倍增/动态规划

主要算法:

1.朴素(暴力搜索)//略过不表

2.线段树

3.ST(Sparse-Table)算法(动态规划)

线段树:线段树能在对数时间logN在数组区间上进行更新与查询。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

线段树是一种二叉搜索树,与区间树相似,将一个区间划分为一些单元区间,每个单元区间对应线段树中的一个叶节点。对于线段树中的每一个非叶子节点[i,j],定义如下:

第一个节点维护区间[i,j]的信息

if i<j,那么左孩子维护区间[i,(i+j)/2]的信息,右孩子维护区间[(i+j)/2+1,j]的信息。

线段树至少支持下列操作:

insert(t,x):将包含区间int的元素x插入到树t中;

delete(t,x):从线段树t中删除元素x;

search(t,i):返回一个指向树t中元素x的指针。

区间在[1,5]内的线段树 

基本结构:

线段树是建立在线段的基础上,每个结点都代表了一条线段[a , b]。长度为1的线段称为元线段。非元线段都有两个子结点,左结点代表的线段为[a , (a + b ) / 2],右结点代表的线段为[( a + b ) / 2 , b]。

右图就是一棵长度范围为[1 , 5]的线段树。

长度范围为[1 , L] 的一棵线段树的深度为log ( L - 1 ) + 1。这个显然,而且存储一棵线段树的空间复杂度为O(L)。

线段树支持最基本的操作为插入和删除一条线段。下面以插入为例,详细叙述,删除类似。

将一条线段[a , b] 插入到代表线段[l , r]的结点p中,如果p不是元线段,那么令mid=(l+r)/2。如果b<mid,那么将线段[a , b] 也插入到p的左儿子结点中,如果a>mid,那么将线段[a , b] 也插入到p的右儿子结点中。

插入(删除)操作的时间复杂度为O (Log N)。

实际应用:

上面的都是些基本的线段树结构,但只有这些并不能做什么,就好比一个程序有输入没输出,根本没有任何用处。

最简单的应用就是记录线段有否被覆盖,并随时查询当前被覆盖线段的总长度。那么此时可以在结点结构中加入一个变量int count;代表当前结点代表的子树中被覆盖的线段长度和。这样就要在插入(删除)当中维护这个count值,于是当前的覆盖总值就是根节点的count值了。

另外也可以将count换成bool cover;支持查找一个结点或线段是否被覆盖。[1]

实际上,通过在结点上记录不同的数据,线段树还可以完成很多不同的任务。例如,如果每次插入操作是在一条线段上每个位置均加k,而查询操作是计算一条线段上的总和,那么在结点上需要记录的值为sum。

这里会遇到一个问题:为了使所有sum值都保持正确,每一次插入操作可能要更新O(N)个sum值,从而使时间复杂度退化为O(N)。

解决方案是Lazy思想:对整个点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询操作的需要分成两部分。

根据Lazy思想,我们可以在不代表原线段的结点上增加一个值toadd,即为对这个结点,留待以后执行的插入操作k值的总和。对整个结点插入时,只更新sum和toadd值而不向下进行,这样时间复杂度可证明为O(logN)。

对一个toadd值不为0的结点整个进行查询时,直接返回存储在其中的sum值;而若对其一部分进行查询,则要更新其左右子结点的sum值,然后把toadd值传递下去,再对这个查询本身,左右子结点分别递归下去。时间复杂度也是O(logN)。

ST算法:

关于ST算法,实际上它本身并不难,它的思想是动态规划。主要用来求RMQ问题,时间复杂度为O(NlgN+M)

关于RMQ问题描述:

输入N个数和M次询问,每次询问一个区间[L,R],求第L个数到R个数之间的最大值,或者是求最小值。

它的原理阐述如下:

对于一个数组A[0...N-1],我们用f[i][j]表示A[i]到A[i+2^j-1],这个范围内的最大值。

由于此区间的元素个数很明显为2^j个,所以我们又可以从中间平分为两部分,这样每部分又有2^(j-1)个元素,这样我们就知道区间[i,i+2^j-1]可以分为[i,i+2^(j-1)-1]和[i+2^(j-1),i+2^j-1]两部分,我们只需要求出后面两个区间最大值的较大值,就可以知道前面区间的最大值了。

所以到了这里,很明显可以写出状态转移方程:

f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])

当然很明显知道初始化f[i][0]=A[i]

当然上面i,j的范围是多少呢?

现在我们来分析一下:我们已经说了如果用上述原理一个区间的元素是2^j个,而可以知道2^j<=N的,所以这样就得到j<=log(N)/log(2);  当然j还大于等于1

对于i,就直接有i+2^j-1<N就行了。

到了这里,我们就可以把f[i][j]求出来了。

接下来就是query()了。

这个怎么办呢,其实很容易,我们先求出满足条件2^x=R-L+1的最大x

这样我们我们就可以把区间[L,R]求最值问题转化为了求区间[L,L+2^x-1]和区间[R-2^x+1,R]最大值的较大值了,为什么可以这样做,因为这两个区间中间有重叠。

但是这两个区间的并一定等于区间[L,R],所以到了这里ST算法的原理基本常阐述完毕了。

剩下的就是代码实现了。

#include <stdio.h>
#include <math.h>
#define N 1005 int m,n;
int a[N];
int f[N][N]; int max(int a,int b)
{
return a>b? a:b;
} void ST()
{
int i,j;
for(i=;i<n;i++)
f[i][]=a[i];
for(j=;j<=(int)((log((double)n)/log(2.0)));j++)
{
for(i=;i+(<<j)-<n;i++)
f[i][j]=max(f[i][j-],f[i+(<<(j-))][j-]);
}
} int query(int L,int R)
{
int x=(int)(log((double)(R-L+))/log(2.0));
return max(f[L][x],f[R-(<<x)+][x]);
} int main()
{
int i,L,R;
while(~scanf("%d%d",&n,&m))
{
for(i=;i<n;i++)
scanf("%d",&a[i]);
ST();
while(m--)
{
scanf("%d%d",&L,&R);
printf("%d\n",query(L-,R-));
}
}
return ;
}

RMQ问题心得的更多相关文章

  1. RabbitMQ学习之:(一)初识、概念及心得

    因为网上有一篇很好的RMQ的入门帖子http://lostechies.com/derekgreer/tag/rabbitmq/,所以我就不多说了,就说说我目前看了该作者1~5章后,自己的心得.(所以 ...

  2. 线段树:CDOJ1591-An easy problem A (RMQ算法和最简单的线段树模板)

    An easy problem A Time Limit: 1000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Pr ...

  3. HDU3183 RMQ/贪心

    A Magic Lamp Problem Description Kiki likes traveling. One day she finds a magic lamp, unfortunately ...

  4. 我的MYSQL学习心得(一) 简单语法

    我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  5. NoSql数据库使用半年后在设计上面的一些心得

    NoSql数据库这个概念听闻许久了,也陆续看到很多公司和产品都在使用,优缺点似乎都被分析的清清楚楚.但我心里一直存有一个疑惑,它的出现究竟是为了解决什么问题? 这个疑惑非常大,为此我看了很多分析文章, ...

  6. 我的MYSQL学习心得(二) 数据类型宽度

    我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  7. 我的MYSQL学习心得(三) 查看字段长度

    我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  8. 我的MYSQL学习心得(四) 数据类型

    我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(五) 运 ...

  9. 我的MYSQL学习心得(五) 运算符

    我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

随机推荐

  1. doget,doPost在底层走的是service

    doget,doPost在底层走的是service 因为在源码上 先执行service方法 然后再调用doget,doPost方法

  2. hdu 1267 下沙的沙子有几粒? (递推)

    下沙的沙子有几粒? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  3. C# 获取ORACLE SYS.XMLTYPE "遇到不支持的 Oracle 数据类型 USERDEFINED"

    1.需要加函数 2.需要加表别名 select   a.XML.getclobval()  from TB1  a

  4. [HAOI2010]计数

    题面在这里 description 你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数. 比如说给定{1,2},那么可以生成数字12,21,102,120,201,210 ...

  5. 周记【距gdoi:117天】

    国庆被“吞”了 图论还剩下平面图.分层图.欧拉图…… 是现实太残酷还是自己兴趣不够? 努力吧.

  6. 容器(vector)、数组、new创建的动态数组,你到底用哪一个(执行效率分析)

    1.问题的提出 在没有了解vector之前,动态数组一般都是又new创建的.在了解vector后发现vector竟是那样方便好用,但方便的同时却是以牺牲执行效率为代价的.网上对vector和array ...

  7. 【ZJ选讲·字符串折叠】

    给一个字符串(len<=100) 把这个字符串折叠(就是压缩) 记 X(子串) 表示重复 X次该子串 比如 3(orz)  orzorzorz  来点神奇例子: AAAAAAAAAA ...

  8. 【NOIP模拟赛】chess 建图+spfa统计方案数

    似乎弗洛伊德和迪杰斯特拉都干不了统计方案数,spfa的话就是不断入队就好. #include <cstdio> #include <cstring> #include < ...

  9. Codeforces Round #520 (Div. 2) B. Math

    B. Math time limit per test:1 second memory limit per test:256 megabytes Description: JATC's math te ...

  10. ionic安装遇到的一些问题

    ionic = Cordova + Angular + ionic CSS // 安装(失败的话 Mac 尝试使用 sudo,Windows 尝试管理员身份运行 cmd)$ npm install - ...