RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列a,回答若干询问RMQ(A,i,j)(i, j<=n),返回数列a中下标在i,j之间的最小/大值。如果只有一次询问,那样只有一遍for就可以搞定,但是如果有许多次询问就无法在很快的时间处理出来。在这里介绍一个在线算法。所谓在线算法,是指用户每输入一个查询便马上处理一个查询。该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

步骤如下:

假设a数组为:

1, 3, 6, 7, 4, 2, 5

1.首先做预处理(以处理区间最小值为例)

设mn[i][j]表示从第i位开始连续2^j个数中的最小值。例如mn[2][1]为第2位数开始连续2个的数的最小值,即3, 6之间的最小值,即mn[2][1] = 3;

之后我们很容想到递推方程:

mn[i][j] = min(mn[i][j - 1], mn[i + (1 << j - 1)][j - 1])

附上伪代码:

for(int j = 0; j < 20; j ++)
for(int i = 1; i + (1 << j) <= n + 1; i ++)
mn[i][j] = min(mn[i][j - 1], mn[i + (1 << (j - 1))][j - 1]);

咦?为什么第二行是i + (1 << j) <= n + 1呢?因为mn[i][j]表示连续2^j个数,所以mn[i][j]所维护的区间为[i, i + (1 << j) - 1],所以在最后要+1,其实是为了方便,写成i + (1 << j) - 1 <= n感觉左边太长了,所以写在右边了。

那么为什么j要写在外围?如果写在里面的输出结果是这样的

我们会发现没有更新过,这是为什么呢? 因为我们在更新的时候是通过要通过2^(j - 1)的区间来更新2^j的区间,来看状态转移方程:

mn[i][j] = min(mn[i][j - 1], mn[i + (1 << j - 1)][j - 1])

我们发现如果j写在里面的话,在更新mn[i][j]的时候会发现mn[i +(1<<j - 1)][j - 1]还没有更新,所以才会出现这样的结果,正确结果如下:

咦?为什么还有0?我们来看伪代码:

for(int j = 0; j < 20; j ++)
for(int i = 1; i + (1 << j) <= n + 1; i ++)
mn[i][j] = min(mn[i][j - 1], mn[i + (1 << (j - 1))][j - 1]);

看第二行会发现,对于i + (1  << j) - 1超过n的,我们没有更新,如图中的mn[5][2],5 + 2^2 - 1 = 8 > 7所以没有更新,但这并不影响询问的结果。

2.查询

假设我们需要查询区间[l, r]中的最小值,令k = log2(r - l + 1); 则区间[l, r]的最小值RMQ[l,r] = min(mn[l][k], mn[r - (1 << k) + 1][k]);

那么为什么这样就可以保证为区间最值吗?

mn[l][k]维护的是[l, l + 2 ^ k - 1], mn[r - (1 << k) + 1][k]维护的是[r - 2 ^ k + 1, r] 。

那么我们只要保证r - 2 ^ k + 1 <= l + 2 ^ k - 1就能保证RMQ[l,r] = min(mn[l][k], mn[r - (1 << k) + 1][k]);

我们用分析法来证明下:

若r - 2 ^ k + 1 <= l + 2 ^ k - 1;

则r - l + 2 <= 2 ^ (k + 1);

又因为 k = log2(r - l + 1);

则r - l + 2 <= 2 *(r - l + 1);

则r - l >= 0;

显然可得。

由此得证。

我们来举个例子 l = 4, r = 6;

此时k = log2(r - l + 1) = log2(3) = 1;

所以RMQ[4, 6] = min(mn[4][1], mn[5][1]);

mn[4][1] = 4, mn[5][1] = 2;

所以RMQ[4, 6] = min(mn[4][1], mn[5][1]) = 2;

我们很容易看出来了答案是正确的。

附上总代码:(以结构体的形式写出):

 #include <cstdio>
#include <algorithm>
using namespace std;
const int N = + ; int a[N]; int mn[N][]; int n, q, l, r; struct RMQ{
int log2[N];
void init(){
for(int i = ; i <= n; i ++)log2[i] = (i == ? - : log2[i >> ] + );
for(int j = ; j < ; j ++)
for(int i = ; i + ( << j) <= n + ; i ++)
mn[i][j] = min(mn[i][j - ], mn[i + ( << j - )][j - ]);
}
int query(int ql, int qr){
int k = log2[qr - ql + ];
return min(mn[ql][k], mn[qr - ( << k) + ][k]);
}
}rmq; void work(){
rmq.init();
scanf("%d", &q);
while(q --){
scanf("%d%d", &l, &r);
printf("%d\n", rmq.query(l, r));
}
} int main(){
while(scanf("%d", &n) == ){
for(int i = ; i <= n; i ++)scanf("%d", a + i), mn[i][] = a[i];
work();
}
return ;
}

参考论文:http://blog.csdn.net/niushuai666/article/details/6624672/

RMQ(ST算法)的更多相关文章

  1. 求解区间最值 - RMQ - ST 算法介绍

    解析 ST 算法是 RMQ(Range Minimum/Maximum Query)中一个很经典的算法,它天生用来求得一个区间的最值,但却不能维护最值,也就是说,过程中不能改变区间中的某个元素的值.O ...

  2. [POJ3264]Balanced Lineup(RMQ, ST算法)

    题目链接:http://poj.org/problem?id=3264 典型RMQ,这道题被我鞭尸了三遍也是醉了…这回用新学的st算法. st算法本身是一个区间dp,利用的性质就是相邻两个区间的最值的 ...

  3. 【原创】RMQ - ST算法详解

    ST算法: ID数组下标: 1   2   3   4   5   6   7   8   9    ID数组元素: 5   7   3   1   4   8   2   9   8 1.ST算法作 ...

  4. HDU 3183 - A Magic Lamp - [RMQ][ST算法]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183 Problem DescriptionKiki likes traveling. One day ...

  5. POJ 3264 Balanced Lineup RMQ ST算法

    题意:有n头牛,编号从1到n,每头牛的身高已知.现有q次询问,每次询问给出a,b两个数.要求给出编号在a与b之间牛身高的最大值与最小值之差. 思路:标准的RMQ问题. RMQ问题是求给定区间内的最值问 ...

  6. 关于基础RMQ——ST算法

    RMQ,Range Maximum/Minimum Query,顾名思义,就是询问某个区间内的最大值或最小值,今天我主要记录的是其求解方法--ST算法 相对于线段树,它的运行速度会快很多,可以做到O( ...

  7. POJ 3368 Frequent values RMQ ST算法/线段树

                                                         Frequent values Time Limit: 2000MS   Memory Lim ...

  8. RMQ st算法 区间最值模板

    #include<bits/stdc++.h> ; ; int f[N][Logn],a[N],lg[N],n,m; int main(){ cin>>n>>m; ...

  9. RMQ问题(线段树+ST算法)

    转载自:http://kmplayer.iteye.com/blog/575725 RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ ...

  10. RMQ问题(线段树算法,ST算法优化)

    RMQ (Range Minimum/Maximum Query)问题是指: 对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小(大)值 ...

随机推荐

  1. input的file 控件及美化

    在一些网站进行上传时,当单击了“浏览”按钮之后会弹出[选择文件]的对话框.想要实现这一功能,用input的file控件来实现就好啦~ <!doctype html> <html la ...

  2. [Linux 性能检测工具]IOSTAT

    IOSTAT NAME:          Iostat, 报告CPU的统计,和 I/O的统计. 语法: iostat  [ -c ] [ -d ] [ -N ] [ -n ] [ -h ] [ -k ...

  3. TSQL--临时表和表变量

    1. 临时表适用数据量较大的情况,因为临时表可以建立索引 2. 表变量适用于数据较小的情况,表变量只能在定义时创建约束(PRIMARY KEY/UNIQUE)从而间接建立索引 3. 临时表是事务性的, ...

  4. SQL Server 中的逻辑读与物理读

    首先要理解逻辑读和物理读: 预读:用估计信息,去硬盘读取数据到缓存.预读100次,也就是估计将要从硬盘中读取了100页数据到缓存. 物理读:查询计划生成好以后,如果缓存缺少所需要的数据,让缓存再次去读 ...

  5. 在JSP动态网页中输出九九乘法表

    <% StringBuffer sb = new StringBuffer(); ; i <= ; i++){ ; j <= i; j++){ sb.append(j + " ...

  6. Ambari工具之认识学习01

    随着大数据的越发流行,大大小小的互联网公司都开始参与一些大数据相关的业务,都想从中分一杯羹.Hadoop是大数据的典型代表,也可以说现在的领头大哥,很多互联网巨头都在使用hadoop,包括BAT等大公 ...

  7. Ineedle驱动方式dpdk测试性能

    这次主要是测试在dpdk方案下,ineedle的处理包的性能. 发包工具: 使用立永当时写的一个发包工具:linux_pcap 做法:大概是从网上抓取了一些数据包,将源ip替换为随即ip,sip替换为 ...

  8. C语言中不同函数之间怎么传值?

    #include <stdio.h> int change(); int change(int j) { j=; return(j); } void main() { int b = ch ...

  9. ELF Format 笔记(六)—— 字符串表

    ilocker:关注 Android 安全(新入行,0基础) QQ: 2597294287 字符串表中包含若干以 null 结尾的字符串,这些字符串通常是 symbol 或 section 的名字.当 ...

  10. 301重定向.htaccess规则(含二级目录跳转二级域名)

    301重定向是一种非常重要的"自动转向"技术.网址重定向最为可行的一种办法.当用户或搜索引擎向网站服务器发出浏览请求时,服务器返回的HTTP数据流中头信息(header)中的状态码 ...