RMQ 算法入门

1. 概述

RMQ(Range Minimum/Maximum Query)。即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。

这两个问题是在实际应用中常常遇到的问题。以下介绍一下解决这两种问题的比較高效的算法。当然,该问题也能够用线段树(也叫区间树)解决,算法复杂度为:O(N)~O(logN),这里我们暂不介绍。

2.RMQ算法

对于该问题,最easy想到的解决方式是遍历,复杂度是O(n)。但当数据量非常大且查询非常频繁时。该算法无法在有效的时间内查询出正解。

本节介绍了一种比較高效的在线算法(ST算法)解决问题。所谓在线算法,是指用户每输入一个查询便立即处理一个查询。该算法一般用较长的时间做预处理。待信息充足以后便能够用较少的时间回答每一个查询。ST(Sparse Table)算法是一个很有名的在线处理RMQ问题的算法。它能够在O(nlogn)时间内进行预处理。然后在O(1)时间内回答每一个查询。

(一)首先是预处理,用动态规划(DP)解决。

设A[i]是要求区间最值的数列。F[i, j]表示从第i个数起连续2^j个数中的最大值。(DP的状态)

比如:

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;

而且我们能够easy的看出F[i,0]就等于A[i]。(DP的初始值)

这样。DP的状态、初值都已经有了,剩下的就是状态转移方程。

我们把F[i。j]平均分成两段(由于f[i,j]一定是偶数个数字)。从 i 到i + 2 ^ (j - 1) - 1为一段。i + 2 ^ (j - 1)到i + 2 ^ j - 1为一段(长度都为2 ^ (j - 1))。

用上例说明,当i=1。j=3时就是3,2,4,5 和 6,8,1,2这两段。

F[i,j]就是这两段各自最大值中的最大值。于是我们得到了状态转移方程F[i, j]=max(F[i,j-1],
F[i + 2^(j-1),j-1])。

代码例如以下:

    void RMQ(int num) //预处理->O(nlogn)
    {
        for(int j = 1; j < 20; ++j)
            for(int i = 1; i <= num; ++i)
                if(i + (1 << j) - 1 <= num)
                {
                    maxsum[i][j] = max(maxsum[i][j - 1], maxsum[i + (1 << (j - 1))][j - 1]);
                    minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
                }
    }  

这里我们须要注意的是循环的顺序。我们发现外层是j,内层所i,这是为什么呢?能够是i在外,j在内吗?

答案是不能够。由于我们须要理解这个状态转移方程的意义。

状态转移方程的含义是:先更新全部长度为F[i,0]即1个元素,然后通过2个1个元素的最值,获得全部长度为F[i,1]即2个元素的最值,然后再通过2个2个元素的最值。获得全部长度为F[i,2]即4个元素的最值,以此类推更新全部长度的最值。

而假设是i在外,j在内的话,我们更新的顺序就是F[1,0],F[1,1],F[1,2],F[1,3],表示更新从1開始1个元素。2个元素,4个元素,8个元素(A[0],A[1],....A[7])的最值。这里F[1,3] = max(max(A[0],A[1],A[2],A[3]),max(A[4],A[5],A[6],A[7]))的值。可是我们根本没有计算max(A[0],A[1],A[2],A[3])和max(A[4],A[5],A[6],A[7]),所以这个方案肯定是错误的。

为了避免这种错误,一定要好好理解这个状态转移方程所代表的含义。

(二)然后是查询。

假如我们须要查询的区间为(i,j),那么我们须要找到覆盖这个闭区间(左边界取i。右边界取j)的最小幂(能够反复。比方查询5,6。7,8,9,我们能够查询5678和6789)。

由于这个区间的长度为j - i + 1,所以我们能够取k=log2( j - i + 1)。则有:RMQ(A, i, j)=max{F[i , k], F[ j - 2 ^ k + 1, k]}。

举例说明。要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2])。

在这里我们也须要注意一个地方。就是<<运算符和+-运算符的优先级。

比方这个表达式:5 - 1 << 2是多少?

答案是:4 * 2 * 2 = 16。

所以我们要写成5 - (1 << 2)才是5-1 * 2 * 2 = 1。

时间: 2024-12-11 11:31:30

RMQ 算法入门的相关文章

谈谈RMQ算法

没用的话:好像好久没更博了,无聊就讲讲算法吧(主要找不到水题). 感觉针对初学者,老师教这个算法时没怎么懂,最近(大概1.2个月前吧)老师又教了lca(最近公共祖先,额,可以百度,我就不讲了,可能以后会再写一篇博客关于这个)讲到lca转RMQ才又回来认真复(xue)习(xi).大概搞懂了,本质是dp. 认识RMQ: 要学习RMQ,首先要知道RMQ求什么吧? RMQ简单来说就是求区间的最大值(最小值). 什么?没懂! 举个栗子: 1  -2  9  10  15  38  -9 这里有 7 个数(

初识RMQ算法

这个RMQ算法是专门针对于求最值的高效算法.其思路比较简单,先是利用DP预处理,之后便是查询,方法如下: 假如我们需要查询的区间为(i,j),那么我们需要找到覆盖这个闭区间(左边界取i,右边界取j)的最小幂(可以重复,比如查询5,6,7,8,9,我们可以查询5678和6789). 因为这个区间的长度为j - i + 1,所以我们可以取k=log2( j - i + 1),则有:RMQ(A, i, j)=max{F[i , k], F[ j - 2 ^ k + 1, k]}. 举例说明,要求区间[

第二章 算法入门 合并排序

在第二章中难的算法不多,接下来我会把稍微复杂一点的算法整理一下 #include <iostream> using namespace std; void mergeSort(int *A,int left,int mid,int right) { int *L=new int[mid-left+1]; int *R=new int[right-mid+1]; int i,j; for(i=0;i<mid-left+1;i++) { L[i]=A[left+i]; } for (j=0;

RMQ算法

上午在做一个题,结果怎么也做不出来,然后看了篇博客,发现其在求最小的字典序的时候用到了RMQ算法. 问徐大佬这是个什么东西,大佬说:你们肯定学过,这个东西1个月以前60级的就问过我了. 为了不让学弟学妹落下,我决定还是学学RMQ算法吧.. one  前言 RMQ(Range Minimum/Maximum Query),是指区间查询最值的算法,我们对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n)是求在序列A中,在区间i,j中出现的最小/最大值. two  RMQ算法 对于该

算法入门心得

最近看了刘汝佳写的算法入门经典  感觉自己的基础实在太烂了  所以总结了一些我们比较容易犯的错误给大家看看(大牛就不要看了O(∩_∩)O) 1.浮点数:两个整数计算的是浮点数的时候一定要把整数先化为浮点数  5-0.1计算机先把5变5.0之后再进行计算的 2.异或运算:这个是个很神秘的东西如果要在一个数组中找一个只有一个的数那么就可以用异或了 异或也可以用在两个数的交换a = a^b; b = a ^ b; a = a ^ b;就实现转换了 自己异或自己等于0: 3.逻辑运算符都是短路运算符a|

Cordic 算法入门

三角函数计算,Cordic 算法入门 三角函数的计算是个复杂的主题,有计算机之前,人们通常通过查找三角函数表来计算任意角度的三角函数的值.这种表格在人们刚刚产生三角函数的概念的时候就已经有了,它们通常是通过从已知值(比如sin(π/2)=1)开始并重复应用半角和和差公式而生成. 现在有了计算机,三角函数表便推出了历史的舞台.但是像我这样的喜欢刨根问底的人,不禁要问计算机又是如何计算三角函数值的呢.最容易想到的办法就是利用级数展开,比如泰勒级数来逼近三角函数,只要项数取得足够多就能以任意的精度来逼

算法入门经典第六章 例题6-14 Abbott的复仇(Abbott&#39;s Revenge)BFS算法实现

Sample Input 3 1 N 3 3 1 1 WL NR * 1 2 WLF NR ER * 1 3 NL ER * 2 1 SL WR NF * 2 2 SL WF ELF * 2 3 SFR EL * 0 Sample Output (3,1) (2,1) (1,1) (1,2) (2,2) (2,3) (1,3) (1,2) (1,1) (2,1) (2,2) (1,2) (1,3) (2,3) (3,3) 析 题目的大意是,输入起点,离开起点时的朝向和终点,求一条最短路. 每一个

写给嵌入式程序员的循环冗余校验(CRC)算法入门引导

写给嵌入式程序员的循环冗余校验(CRC)算法入门引导 http://blog.csdn.net/liyuanbhu/article/details/7882789 前言 CRC校验(循环冗余校验)是数据通讯中最常采用的校验方式.在嵌入式软件开发中,经常要用到CRC 算法对各种数据进行校验.因此,掌握基本的CRC算法应是嵌入式程序员的基本技能.可是,我认识的嵌入式程序员中能真正掌握CRC算法的人却很少,平常在项目中见到的CRC的代码多数都是那种效率非常低下的实现方式. 其实,在网上有一篇介绍CRC

算法导论(Introduction to Algorithms )— 第二章 算法入门 — 2.1 插入排序

一.插入排序:INSERTION-SORT 1.适用范围: which is an efficient algorithm for sorting a small number of elements. 对于少量元素的排序,插入排序是一种高效的算法. 2.原理: Insertion sort works the way many people sort a hand of playing cards. We start with an empty left hand and the cards