【概述】

RMQ(Range Minimum/Maximum Query),是对于长度为 n 的数列 A,回答若干次询问 RMQ(i,j),返回数列 A 中下标在区间 [i,j] 中的最值,即:区间最值查询问题

目前常用于解决 RMQ 问题的方法为 ST 算法(Sparse Table),利用 ST 算法预处理打出的表,称为 ST 表

【ST 算法】

对于 RMQ 问题,给出 n 个数 m 次询问,每次询问区间最值,当 m 较小时,使用暴力即可解决,但随着 m 的增大,O(logn) 的的询问处理已经不够,需要 O(1) 的询问。

而 ST 算法可以在 O(nlogn) 时间内进行预处理,然后在 O(1) 时间内回答每个查询,其实际上就是一种动态规划与打表的思想。

1.原理

1)预处理

要在 O(1) 求出区间的最值,一个很自然的想法是用动态规划处理的方法,用 dp[i][j] 来记录区间 [i,j] 的最大值,这样显然有状态转移方程:dp[i][j]=max(dp[i][j-1],a[j]),但这样预处理是 O(n*n) 的,还能进一步的优化。

max 函数满足一个性质:允许区间重叠,即 max(i,j,k)=max( max(i,j) , max(j,k) ),也就是说,可以由两个较小的有重叠的区间,直接推出一个大区间,从而减少维护的区间数量。

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

可以看出 F[i,0] = A[i](DP的初始值)

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

于是得到:F[i,j] = max( F[i , j-1] , F[i+2^(j-1) , j-1] )(状态转移方程)

例如:

A 为:3 2 4 5 6 8

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

2)查询

假设要查询的区间为 (i,j),那么需要找到覆盖这个闭区间 [i,j] 的最小幂,由于区间长度为 j-i+1,因此可以取 k=log2(j-i+1),则有:RMQ(i, j) = max{ F[i,k] , F[ j-2^k+1,k] }

例:要求区间 [1,5] 的最大值

有:k = log2(5-1+1) = 2

则:RMQ(1,5) = max( F[1,2] , F[5-2^2+1, 2]) = max(F[1,2] , F[2,2])

2.实现


int dpMax[N][20];
int dpMin[N][20];
int a[N];
void initMax(int n){//初始化最大值查询for(int i=1;i<=n;i++)dpMax[i][0]=a[i];for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++)dpMax[i][j]=max(dpMax[i][j-1],dpMax[i+(1<<(j-1))][j-1]);
}
int getMax(int L,int R){//查询最大值//int k = (int)(log10(R-L+1)/log10(2));int k=0;while((1<<(k+1))<=R-L+1)k++;return max(dpMax[L][k],dpMax[R-(1<<k)+1][k]);
}void initMin(int n){//初始化最小值查询for(int i=1;i<=n;i++)dpMin[i][0]=a[i];for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++)dpMin[i][j]=min(dpMin[i][j-1],dpMin[i+(1<<(j-1))][j-1]);
}
int getMin(int L,int R){//查询最小值//int k = (int)(log10(R-L+1)/log10(2));int k=0;while((1<<(k+1))<=R-L+1)k++;return min(dpMin[L][k],dpMin[R-(1<<k)+1][k]);
}
int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&a[i]);initMax(n);initMin(n);for(int i=1;i<=m;i++){int left,right;scanf("%d%d",&left,&right);int maxx=getMax(left,right);int minn=getMin(left,right);printf("Max=%d\n",maxx);printf("Min=%d\n",minn);}return 0;
}

【二维 RMQ】

一维 RMQ 问题是求一个数列 A 中的最值,而二维 RMQ 问题是求一个 n*m 的矩阵中,某个子矩阵内的最值

1.原理

1)初始化

设 F[i][j][ii][jj] = x 表示以 (i, j) 为左上角,以 (i+(1<<ii)-1, j+(1<<jj)-1) 为右下角的矩阵内的最大值(DP的状态)

易知:F[i][j][0][0] = G[i][j](DP的初始值)

假设 F[i][j][ii][jj] 中的 ii 不为 0,那么 F[i][j][ii][jj] = max(F[i][j][ii-1][jj], F[i+(1<<ii)][j][ii-1][jj] ) (状态转移方程)

如果 ii 为 0,那么就按 jj 来求,即 F[i][j][ii][jj] = max(F[i][j][ii][jj-1] , F[i][j+(1<<(jj-1))][ii][jj-1]) (状态转移方程)

简单来说,就是将二维问题转变为一维问题求解

2)查询

对于一个以 (x1,y1) 为左上角,以 (x2,y2) 为右下角的矩形,将其分成四小块,这四小块可能有重合部分,但他们共同构成了目标矩形:

  • F[x1][y1][ii][jj]
  • F[x1][y2-(1<<jj)+1][ii][jj]
  • F[x2-(1<<ii)+1][y1][ii][jj]
  • F[x2-(1<<ii)+1][y2-(1<<jj)+1][ii][jj]

那么,可先求解第一、二小块的最值,第三、四小块的最值,最后再求整体最值

即:

  • temp1 = max(F[x1][y1][ii][jj] , F[x1][y2-(1<<jj)+1][ii][jj])
  • temp2 = max(F[x2-(1<<ii)+1][y1][ii][jj] ,F[x2-(1<<ii)+1][y2-(1<<jj)+1][ii][jj] )

最终结果为:res = max(temp1,temp2)

2.实现

int G[N][N];
int dpMin[N][N][10][10];
int dpMax[N][N][10][10];
void initRMQ(int n,int m){//对n*m的矩阵初始化RMQ且矩阵下标从1开始for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)dpMin[i][j][0][0]=dpMax[i][j][0][0]=G[i][j];for(int ii=0;(1<<ii)<=n;ii++){for(int jj=0;(1<<jj)<=m;jj++){if(ii+jj){for(int i=1;i+(1<<ii)-1<=n;i++){for(int j=1;j+(1<<jj)-1<=m;j++){if(ii){dpMin[i][j][ii][jj] = min(dpMin[i][j][ii-1][jj] , dpMin[i+(1<<(ii-1))][j][ii-1][jj]);dpMax[i][j][ii][jj] = max(dpMax[i][j][ii-1][jj] , dpMax[i+(1<<(ii-1))][j][ii-1][jj]);}else{dpMin[i][j][ii][jj] = min(dpMin[i][j][ii][jj-1] , dpMin[i][j+(1<<(jj-1))][ii][jj-1]);dpMax[i][j][ii][jj] = max(dpMax[i][j][ii][jj-1] , dpMax[i][j+(1<<(jj-1))][ii][jj-1]);}}}}}}
}
int getMax(int x1,int y1,int x2,int y2){//RMQ查询最大值int k1=0,k2=0;while((1<<(k1+1))<=x2-x1+1)k1++;while((1<<(k2+1))<=y2-y1+1)k2++;x2=x2-(1<<k1)+1;y2=y2-(1<<k2)+1;int temp1=max(dpMax[x1][y1][k1][k2],dpMax[x1][y2][k1][k2]);int temp2=max(dpMax[x2][y1][k1][k2],dpMax[x2][y2][k1][k2]);return max(temp1,temp2);
}
int getMin(int x1,int y1,int x2,int y2){//RMQ查询最小值int k1=0,k2=0;while((1<<(k1+1))<=x2-x1+1)k1++;while((1<<(k2+1))<=y2-y1+1)k2++;x2=x2-(1<<k1)+1;y2=y2-(1<<k2)+1;int temp1=min(dpMin[x1][y1][k1][k2],dpMin[x1][y2][k1][k2]);int temp2=min(dpMin[x2][y1][k1][k2],dpMin[x2][y2][k1][k2]);return min(temp1,temp2);
}
int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&G[i][j]);initRMQ(n,m);int q;scanf("%d",&q);while(q--){int r1,c1,r2,c2;scanf("%d%d%d%d",&r1,&c1,&r2,&c2);int maxx=getMax(r1,c1,r2,c2);int minn=getMin(r1,c1,r2,c2);printf("max=%d\n",maxx);printf("min=%d\n",minn);}return 0;
}

【例题】

  1. Find the hotel(HDU-3193)(一维 RMQ):点击这里
  2. Check Corners(HDU-2888)(二维 RMQ):点击这里
  3. 平衡的阵容(洛谷-P2880)(RMQ 求极差):点击这里
    同题:Balanced Lineup(POJ-3264):点击这里
  4. A Magic Lamp(HDU-3183)(RMQ+模拟 ):点击这里

线性结构 —— ST 表与 RMQ相关推荐

  1. 【专题】用ST表解决RMQ刷题总结

    [专题]用ST表解决RMQ刷题总结 看了一下上次写博客居然是好久以前的事了(我真是老懒狗了 ) 开门见山,直接放专题链接和代码 kuangbin rmq专题 这个contest里面一共十道题但是实际上 ...

  2. ST函数(ST表)RMQ O(1)查询 离线

    ST算法是基于倍增的动态规划算法. #include<iostream> #include<cstdio> #include<cstdlib> #include&l ...

  3. 数据结构——线性结构

    简单地说,线性结构就是表中各个结点具有线性关系.如果从数据结构的语言来描述,线性结构应该包括如下几点: 1.线性结构是非空集. 2.线性结构有且仅有一个开始结点和一个终端结点. 3.线性结构所有结点都 ...

  4. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  5. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  6. 数据结构与算法——线性结构——线性表及其表示

    -"一,线性结构 1.顺序储存结构直接表示 多项式. 1).使用数组来表示多项式.(用数组下标来表示指数,值来表示系数) 可以表示成: 2).使用结构数组来表示.(把系数和指数看成一个二元组 ...

  7. 【数据结构总结】第四章:串、数组和广义表(线性结构)

    第四章:串.数组和广义表(线性结构) 提示:本文主要是以思维导图的形式概括数据结构第一章的精华内容,基本不会用到文字性的内容,目的是为了给大家梳理每个重要的知识点的相关概念,方便大家在复盘的时候快速阅 ...

  8. RMQ问题-ST表倍增处理静态区间最值

    简介 ST表是利用倍增思想处理RMQ问题(区间最值问题)的一种工具. 它能够做到O(nlogn)预处理,O(1)查询的时间复杂度,效率相当不错. 算法 1.预处理 ST表利用倍增的思想.以洛谷的P38 ...

  9. 51Nod.1766.树上最远点对(树的直径 RMQ 线段树/ST表)

    题目链接 \(Description\) 给定一棵树.每次询问给定\(a\sim b,c\sim d\)两个下标区间,从这两个区间中各取一个点,使得这两个点距离最远.输出最远距离. \(n,q\leq ...

最新文章

  1. Java 类加载机制详解
  2. 【Android Developers Training】 93. 创建一个空验证器
  3. 【论文解读】Yoshua Bengio最新修改版论文:迈向生物学上可信的深度学习
  4. 比double精度更高的数据类型_MATLAB中图像的数据类型
  5. 字符串得结果!Java数组模拟栈以实现中缀表达式综合计算器,字符串表达式计算器
  6. 面试题19:二叉树镜像
  7. php怎么查询数据库,php怎么查询数据库
  8. Facebook授权登录
  9. Gartner最新洞见:数据分析将是改变“游戏规则”的技术、企业的中台需要可组装的架构
  10. Cookie和Session专题
  11. C# SendMessage 全集
  12. java 僵尸进程_有关僵尸进程和孤儿进程的解释
  13. 2019电子设计大赛:纸张计数显示装置
  14. vs2010字符集问题
  15. 2021 年“认证杯”数学中国数学建模网络挑战赛 B题解题思路
  16. cad2020打印样式放在哪个文件夹_打印机故障:打印一直出现乱码,什么原因?...
  17. 冬瓜哥送你元宵大礼盒啦!!
  18. subclass and extends
  19. OpenJ_Bailian - 2711
  20. android 自定义相册选择,Android图片选择器,支持拍照、从相册选择、裁剪、自定义主题...

热门文章

  1. 以朋友圈为例,腾讯资深架构师揭秘鹅厂大数据平台是怎样运营的
  2. 人工智能的前生,今世,和未来
  3. 我和学姐聊了下他在阿里这9年的成长,受益颇多!
  4. Kotlin开发团队惹上麻烦了
  5. JEECG 深度使用培训班 周六周日公开课(一期班)
  6. Pull Request的正确打开方式(如何在GitHub上贡献开源项目)
  7. 有传闻说,写好总结可以升职加薪?
  8. 编程体系结构(07):JavaEE之Web开发
  9. pandas 基本使用
  10. 好程序员分享居中一个float元素