RMQ问题第一弹



今天,我给大家分享一下我在学习 RMQ 问题过程中对该问题的理解。

RMQ (Range Minimum/Maximum Query ):中文名为“区间最值查询”。RMQ 问题指的是给定一段区间,针对给定区间进行若干次查询,每次给出不同的待查询子区间范围,要求返回子区间内的最大值或者最小值。

一般此类问题会给出区间内总元素个数 n 、待查询的次数 q ,以及每次查询的始末位置。

根据常规的思路,要找出一段区间中的最大值或最小值,我们会采取遍历区间的做法,该方法的核心代码在此不作展示。分析可知,n 个数据的规模,进行 q 次查询,时间复杂度为 o(q*n ),当问题的规模逐渐增大或者查询的次数逐渐增多的时候,我们无法在有限的时间内完成任务。因此,寻找更方便更快捷的办法成为我们的目标。在学习算法的道路上,我们都知道,第一直觉想出来的解决思路往往是最直接也是最低效的,于是理所当然的,我们需要对原来的思路进行改进,甚至摒弃原来低效的方法另辟蹊径。常规的思路时间主要耗费在对最值的查询和求解工作上,我们可以想办法将查询时间缩短,有人提出将可能查询的区间列出来,针对每个区间找出其中的最值存储在数组里,每次查询最值数组即可,这个思路需要存储的量有三个,起始点、终止点、区间最值,可以考虑模仿动态规划的理念,对二维数组的两个下标和存储的值赋予特殊含义,即用二维数组的第一坐标表示起始点、第二坐标表示终止点,那么值就可以表示区间最值。这样一来,查询的时间缩短为 o(1),但是问题在于,该 n* n 的二维数组赋值的时候,如果采用遍历对应区间的方法求最值,整个二维数组赋值过程时间复杂度为 o(n^3),这就意味着,虽然做了多余的工作,也做了合理的设想,但是并未达到优化问题的目的。这是不是意味着当前的思路不可行呢?我们需要先考虑该二维数组赋值过程能不能优化,如果不能,那这个思路就可以宣布失败了。

天无绝人之路,机会总是偏爱大胆实践的人。我们可以在此作出大胆预测,如果采用以上对二维数组的定义:第一坐标表示起始点,第二坐标表示终止点,值表示最值,即使求解最值的方法时间复杂度是 o(1),那求解该二维数组的整个过程时间复杂度也在 o(n^2),我们知道并没有 o(1)求解乱序区间最值的方法,因此,可以得出结论,以上对二维数组的定义无法达到优化的目的。

这就意味着问题不仅出现在求解区间最值的方法上,同时基本的二维数组定义也是不合理的。不合理之处在于规模过大,我们需要想办法缩小二维数组的规模。表示某一区间和其最值除了首末端点和最值三个参数这种方法,还可以用区间起始点、区间长度、区间最值三个参数。区间长度范围是 1 ~ n ,换个思路想一下,表示区间长度时候定义可以是多样的。

由此衍生出第二种方法-- ST 方法。

ST(Sparse Table)方法,sparse 中文译为:稀疏的,稀少的,零星的;table 中文译为:桌子,表,目录,手术台,工作台,游戏台;Sparse Table 具体中文名到底如何翻译,没有一个固定说法,我们暂且称之为 ST方法,该方法的本质是用动态规划方法求出用于查询的最值数组,以待查询子区间的起始位置和区间长度标记一个状态,以该子区间内的最大值或最小值作为状态值。

以求最大值为例: dp[ i ][ j ] = max ,其中,i 表示待查询子区间起始位置 ;j 表示待查询子区间的长度为 2 ^ j , max 表示在该子区间中最大值为 max 。 分析可知:1 <= i <= n , 0 <= j <= log n (以 2 为底), 此时,二维数组的规模为 n *log n (以 2 为底) 。动态规划的表达式有了,现在我们来研究状态转换方程,由 dp 数组的定义可以看出来,dp[ i ][ j ] 表示的区间长度 2 ^ j 一定是偶数,因此,区间可以等分为两个长度为 2 ^( j - 1 ) 的子区间,依照分治的思想,求该区间的最值,可以通过选择两个子区间最值中的最值来实现。此时,状态转换方程为:

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

求最值数组具体的核心代码实现过程如下:

注意,第 13 行的注释“ j 必须为外层循环控制量”,原因在于求解过程中,先求出以各元素为初始元素区间长度为 1 的最值,再求出以各元素为初始元素区间长度为 2 的最值,依次求解。因此 i 为内层循环控制量, j 为外层循环控制量,而不能以 i 为外层循环控制量,因为此时假设 求 dp[ i ] [ j ] ,如果 i 为外层循环控制量,dp[i][j] = max( dp[i][j-1] , dp[i + 2 ^(j-1) - 1][j-1]) ,其中 dp[ i ][ j - 1] 已知 ,但是 dp[i + 2 ^(j-1) - 1][j-1] 未知的,准确说, 第一坐标值 大于 i 的值都是未知的。

查询的时候,已知的信息是始末元素下标 start 和 end ,我们的 dp 数组中存储的是长度为 2 的幂的区间最值,对于长度非 2 的幂的区间,我们需要找到能够覆盖这个区间的两个区间,选取两者的最值即可。

如下,求下标 1 ~12 的元素中的最值,log 12 (以 2 为底)= 3 ,因此选取 F(1,3)和 F(5,3)两个区间,组合起来可以覆盖待求区间的所有数据。

查询部分核心代码如下:

今天的分享就到这里,其他相关的方法且看下篇文章。



感谢大家对我的支持,没关注的朋友可以扫下方二维码关注我呦。

时间: 2024-11-10 01:10:33

RMQ问题第一弹的相关文章

RMQ_第一弹_Sparse Table

title: RMQ_第一弹_Sparse Table date: 2018-09-21 21:33:45 tags: acm RMQ ST dp 数据结构 算法 categories: ACM 概述 RMQ (Range Minimum/Maximum Query) 从英文便可以看出这个算法的主要是询问一个区间内的最值问题,,, 暑假集训的时候学习了 线段树 ,,, 也可以对给定数组查询任意区间的最值问题,,,, 这两个主要的区别就是 线段树 可以进行单点的修改操作,,,而 Sparse Ta

javascript之【贪吃蛇系列】第一弹:简单的贪吃蛇实现

参考博客:http://blog.csdn.net/sunxing007/article/details/4187038 以上博客是参考,毕竟第一次做,真让自己盲人摸象做不出来. 不过我在其上做了一些改进,界面等效果看起来更好一些. 下图是在Chrome上运行的效果,但是火狐和IE会不兼容,onkeydown事件不能正确调用 这里用了一张图把贪吃蛇制作过程的思想画了出来,画的有点简陋: 下面就是把代码发上来,上边有详细的解释: <html> <head> <title>

codechef 营养题 第一弹

第一弾が始まる! 定期更新しない! 来源:http://wenku.baidu.com/link?url=XOJLwfgMsZp_9nhAK15591XFRgZl7f7_x7wtZ5_3T2peHh5XXoERDanUcdxw08SmRj1a5VY1o7jpW1xYv_V1kuYao1Pg4yKdfG4MfNsNAEa codechef problems 第一弹 一.Authentication Failed原题题面Several days ago Chef decided to registe

FluentData 学习 第一弹

地址: http://fluentdata.codeplex.com/ 前世: FluentData 我们公司用的一个增删改查的里面的持久层.之前还不知道 这个持久层叫FluentData.  某天看见群里 说 某视频网站里面 居然在讲这个开发框架,还收费.我搜了一下.fluentdata有源代码. 这个13年有过记载.不过我是新手.什么也需要 学习一下.  和 室友说了一下微型orm ,他们 呢  用的  微型orm是 Dapper .可以去了解一下. 废话真多,完毕. FluentData

C#Light 和 uLua的对比第一弹

初始化 做一样的初始化,其实是没办法对等的 C#Light的Env 不等于Lua的LuaState C#Light的执行有完全的执行堆栈,完整的作用域,lua都没有 C#Light有完整的class 函数,继承特征,lua都没有 C#Light可以用VS做编辑器,lua? 为了保持平等对抗,三个测试先做一遍,再依次执行 测试1 ,简单的一次创建给个名字并销毁,测试300次 测试2,调用Debug.Log打印,执行300次 测试3,一次性在脚本里多调用一些方法,创建10个物体再销毁 测试一结果:

The Internet Communications Engine (Ice) 跨平台异构通讯方案 第一弹-ICE简介

.net中的通讯方案很多,从.net Remoting,MSMQ,Webservice,WSE,WCF等等,他们都有一个特点,易用,但是都不能跨语种异构,如果你服务端要用java开发,客户端用C#开发,或者其它语言譬如C++, Python,PHP, Ruby, Objective-C,等等,那么.net提供的解决方案将不再有效.现在,隆重推出跨平台异构方案ICE.ICE官网:http://www.zeroc.com/ice.html 简介:The Internet Communications

MongoDB第一弹

下载: MongoDB下载地址: http://www.mongodb.org/ 我下载的是windows32的MSI. 安装: next就可以. 新建: 新建data文件夹,在data里面再建db和log两个子目录. 启动: 进入cmd,到当前MongoDB的bin目录下面,启动 "mongd"!(不是mongo),修改db路径. 然后打开浏览器输入localhost:27017,会出现: 最后在cmd中启动mongo.exe,出现: 基本操作: insert: find: upda

Ubuntu闪电入门第一弹

目录: 一.设置主机名 1.临时修改主机名 2.永久修改主机名 二.设置网络 1.配置静态IP地址 2.配置DNS服务器解析 3.修改DNS本地解析 三.软件包管理 1.dpkg管理软件包 2.apt管理软件包 四.运行级别 五.文件传输工具lrszs 1.发送文件sz 2.接收文件rz 六.磁盘管理 1.磁盘分区 2.磁盘分区格式化 3.挂载磁盘分区 一.设置主机名 1.临时修改主机名(重启系统后失效) $ sudo hostname ikki $ hostname ikki 2.永久修改主机

C# 二进制替换第一弹 byte 数组替换

Unity 3D本地发布WebPlayer版时遇到提示:"Failed to download data file"(如上图),遇到这个问题就是指Web服务器并没有支持这种*.unity3d文件类型.需要做的是在Web服务器中添加MIME类型: 1.IIS 7 及以上版本: 在功能视图的IIS选项卡中: 双击打开MIME,选择"添加": .unity3d application/octet-stream 2.IIS6版本: 右击计算机名,选择属性,找到MIME类型,