RMQ(range minimum/maximum query)即查询区间最大最小值。

对于求区间最大最小值,我们自然而然就想到了一个O(n)时间复杂度的算法,但是如果询问有很多呢?这样必然超时。当然我们可以用线段树来解,使得每一次查询的时间降到log(n),但是对于RMQ算法,只要我们做了些预处理,之后的查询我们仅需要O(1)的时间。Sparse_Table算法是解决RMQ问题的一类较好的算法,属于一种在线算法,至于什么叫在线什么叫离线,先简单介绍一下。

在线算法:在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。

离线算法:在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。例如,选择排序在排序前就需要知道所有待排序元素,然而插入排序就不必了。

简单的概括一下 在线算法就是说程序先把预处理工作做好,对于之后的查询,可以很快给你答复。离线算法就是你先把需求告诉程序,他一次性给你解决。

好了,下面来讲解Sparse_Table算法

1.求最值数组

Sparse_Table算法的预处理就是一个动态规划的思想。

设数组maxn[i][j] 表示给定的数组从下标i开始,长度为2^j的区间最大值(最小值一样)也就是arr[i]----arr[i+2^j-1]这个区间的最大值。

于是我们可以写出这样一个动态转移方程maxn[i][j] = max(maxn[ i ][ j-1 ],maxn[ i+2^(j-1) ][ j-1 ]) 看懂了么?

其实就是把区间【i ,i+2^j -1】分成两段,一段是【i,i+2^(j-1)-1】 和【i+2^(j-1),i+2^j】(一直记住二维数组后面一维表示的是区间的长度2^j)

那么对于maxn[i][j]当j等于0,也就是区间长度为1的最大值显然就有maxn[i][0] = arr[i];

到此我们就可以写出Sparse_Table的预处理部分了

[cpp] view plaincopyprint?

  1. void getbestarr(int n)//n为给定的数组的长度
  2. {
  3. int tem = (int)floor(log2((double)n));//因为区间的最长长度是2^tem==n嘛
  4. for(int i=1;i<=n;i++)
  5. minn[i][0]= maxn[i][0] = arr[i];
  6. for(int j=1;j<=tem;j++) //下标从1开始
  7. for(int i=1;i+(1<<j)-1<=n;i++)
  8. {
  9. maxn[i][j] = max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);  //最大值
  10. minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);  //最小值
  11. }
  12. }

我们看看这个动态规划方程是怎么求解这个maxn,minn数组的

要求区间长度为2的肯定要先求出区间长度为1的嘛  比如区间[1,2]只要在区间[1,1]   [2,2]中取最值嘛 长度为4的肯定要先算出长度为2的嘛 比如求区间[6,9]的最值只要在区间[6,7] [8,9]中取最值嘛。。。。。。

所以最外层的循环就肯定是区间的长度2^j次方咯 里面的循环应该都看得懂吧。

2.查询

这个最值区间的数组求出来了,下面就是查询了

比如要查询区间[a,b]的最值  怎么求呢?

注意到我们的最值数组存的都是区间长度为2^k(k=0,1,2,3.....)次方的最值

所以对于区间[a,b] 我们肯定要划分为两个区间长度是2^x  2^y的区间才可以直接利用我们得到的最值数组来求最值嘛

这里有两个未知数不好求,我们可以直接取k,对于k满足a+2^k-1=b
 k=log2(b-a+1) (注意这里不是a+2^k=b 还是那句话,始终记得2^k是区间的元素的个数)
那么区间a,b的最大值不就是max(maxn[a][k],maxn[b-2^k+1][k])比如对于区间长度为4的[3,6]求出k==2
于是最大值就是区间max(【3,6】,【3,6】)当然我们不能能保证log2(a-b+1)就一定能得到一个整数,但是这不要紧,比如对于区间长度为
5的【3,7】我们对log2(7-3+1)取整得到2,于是最大值就在

max(【3,6】,【4,7】),max函数里面前面那个maxn[a][k]就保证了我们的求最值的区间以a开始,后面那个maxn[b-2^k+1][k]就保证了我们必然能够以b为尾

[cpp] view plaincopyprint?

    1. int query(int a,int b,bool getwhat)//getwhat表示你是想取最大还是最小
    2. {
    3. int k = log2(b-a+1);
    4. if(getwhat)
    5. return max(maxn[a][k],maxn[b-(1<<k)+1][k]);
    6. else
    7. return min(minn[a][k],minn[b-(1<<k)+1][k]);
    8. }
时间: 2024-08-01 07:49:37

RMQ(range minimum/maximum query)即查询区间最大最小值。的相关文章

poj 3264 Balanced Lineup【RMQ-ST查询区间最大最小值之差 +模板应用】

题目地址:http://poj.org/problem?id=3264 Sample Input 6 3 1 7 3 4 2 5 1 5 4 6 2 2 Sample Output 6 3 0分析:标准的模板题,可以用线段树写,但用RMQ-ST来写代码比较短.每次输出区间[L, R]内最大值和最小值的差是多少.注意一个地方,代码里面用到了log2()函数,但是我用包含<math.h>和<cmath>头文件的代码以C++的方式提交到POJ反馈是编译错误.改成g++提交才AC了.(注意

poj 3264 区间最大最小值 RMQ问题之Sparse_Table算法

Balanced Lineup Time Limit: 5000 MS Memory Limit: 0 KB 64-bit integer IO format: %I64d , %I64u Java class name: Main [Submit] [Status] [Discuss] Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order

poj--3264Balanced Lineup+ST算法求区间最大最小值

题目链接:点击进入 其实这种动态查询区间最大最小值的题目,解法是有很多的,像是线段树和树状数组都是可以做的.ST算法效率和上面两种是一样的,但是编码更为简单. ST算法是一种利用了递推思想进行计算的算法,令dp(i,j)表示从i开始长度为2^j的一段元素中的最小值,则dp(i,j)=min(dp(i,j-1),dp(i+2^(j-1),j-1)).这是求区间最小值的递归关系,其实求区间最大值也是一样的. 代码如下: #include<iostream> #include<cstring&

Geeks - Range Minimum Query RMQ范围最小值查询

使用线段树预处理,可以使得查询RMQ时间效率在O(lgn). 线段树是记录某范围内的最小值. 标准的线段树应用. Geeks上只有两道线段树的题目了,而且没有讲到pushUp和pushDown操作,只是线段树的入门了. 参考:http://www.geeksforgeeks.org/segment-tree-set-1-range-minimum-query/ 我修改了一下他的程序,使用pushUp操作,其实也是很简单的一个小函数.而且手动计算了下,觉得他的动态分配内存,计算需要的树的大小,这样

AOJ DSL_2_A Range Minimum Query (RMQ)

Range Minimum Query (RMQ) Write a program which manipulates a sequence A = {a0,a1,...,an−1} with the following operations: find(s,t): report the mimimum element in as,as+1,...,at. update(i,x): change ai to x. Note that the initial values of ai (i=0,1

RMQ(Range MinimumQuery)问题

RMQ(Range MinimumQuery)问题 有关RMQ的详细介绍可见刘汝佳<算法竞赛入门经典训练指南>P197页 RMQ问题可以解决对于一个整数数组(当然也可以是其他可比较大小的元素类型)的任意区间[L, R]查询最值时,以O(1)时间复杂度回答询问.其实它就是一种数据压缩的思想. RMQ能在经过O(nlogn)的时间预处理后,做到O(1)时间复杂度的任意区间最大最小值查询. 下面是一维RMQ代码: #include<cstdio> #include<cstring&

hibernate使用Query进行查询

本文主要探讨hibernate的简单查询,主要是使用Query进行的查询. 1.首先看下annotation的API中关于查询的描述 2.3. 映射查询 2.3.1. 映射EJBQL/HQL查询 使用注解还可以映射EJBQL/HQL查询. @NamedQuery 和@NamedQueries是可使用在类和包上的注解. 但是它们的定义在session factory/entity manager factory范围中是都可见的. 命名式查询通过它的名字和实际的查询字符串来定义. javax.per

CDOJ 1104 求两个数列的子列的交集 查询区间小于A的数有多少个 主席树

求两个数列的子列的交集 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/1104 Description 给两个数列A, B,长度分别为n1, n2,保证A中每个元素互不相同,保证B中每个元素互不相同..进行Q次询问,每次查找A[l1...r1]和B[l2..r2]的交集 集合 大小是多少.. 比如 A = {1,2,3,4,5,6,7},B = {7,6,5,4,3,2,1}

!SPOJ 1043 多次查询区间最大连续和-线段树

题意:已知一个数列,现在有多次查询(a,b),查询区间[a,b]的最大连续和. 分析: 这道题没有更新操作,只有区间查询操作.动态在于待查询区间不同,最大连续和也不同.所以其实相当于每次查询的时候要计算一次待查询区间的最大连续和. 有3种情况: 1.待查询区间包含当前区间.那么就直接返回当前区间的最大连续和: 2.待查询区间在当前区间的左区间或右区间.那么在左或右区间递归查询即可: 3.待查询区间横跨当前区间的左右区间.那么:当前区间在左区间部分的最大连续和.在右区间部分的最大连续和.左右区间连