RMQ (Range Minimal Query) 问题 ,稀疏表 ST
RMQ ( 范围最小值查询 ) 问题是一种动态查询问题,它不需要修改元素,但要及时回答出数组 A 在区间 [l, r] 中最小的元素值。
RMQ(Range Minimum/Maximum Query):对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。
对于 RMQ ,我们通常关心两方面的算法效率:预处理时间和查询时间。
解决一般 RMQ 问题的三种方法
胜者树 (Winner Tree) O(n)-O(logn)
稀疏表 (Sparse Table) O(nlogn)-O(1)
线段树 (Segment Tree) O(n)-O(logn)
这里介绍一个稀疏表算法。
一、稀疏表ST算法--预处理
二、稀疏表ST算法--查询
三、RMQ ST模板
package ads; public class RMQ_ST { int[] a;
int[][] st;//st[i][j],i表示数组a的索引为i的元素,即i:索引
int n;
public RMQ_ST(int[] a) {
this.a = a;
this.n = a.length;
st = new int[n][17];
} void initRMQ() {
int k = (int)(Math.log((double)n) / Math.log(2.0));
for (int i=0; i<n; i++) st[i][0] = i;
for (int j=1; j<=k; j++) { //dp开始,从1到k
for (int i=0; i<n; i++) { //遍历每个元素
st[i][j] = st[i][j-1]; //先赋值为前一半的RMQ值
int part = i + (1 << (j-1));//后一半的第一个元素索引
if (part >= n) break; //如果后一半的第一个元素已经超出数组范围,直接跳过后面的元素
if (a[ st[i][j-1] ] > a[ st[part][j-1] ]) st[i][j] = st[part][j-1];
}
}
} int rmq(int x, int y) {
int k = (int)(Math.log((double)y-x+1) / Math.log(2.0));
int part = y - (1<<k) + 1;//切分,比如0~4切分成st(0,2)和st(1,2),前者是0~3的最小元素,后者是1~4的
if (a[ st[x][k] ] < a[ st[part][k] ]) return st[x][k];
return st[part][k];
} public static void main(String[] args) {
int[] numbers = {4,3,2,1,6,7,8,9};
RMQ_ST st = new RMQ_ST(numbers);
st.initRMQ();
System.out.println(st.rmq(0, 3));
System.out.println(st.rmq(4, 7));
} }
四、示例代码
poj2425 http://poj.org/problem?id=2452
思路:枚举每个位置 i ,找出右边第一个比 a[i] 小的元素位置j
在 i 到 j-1 中间求最大值的位置 k ,如果 a[k] > a[i],那么更新答案
Description
Now given the length of S1, S2, S3, …Sn, you are required to find the maximum value j - i.
Input
Line 1: a single integer n (n <= 50000), indicating the number of sticks.
Line 2: n different positive integers (not larger than 100000), indicating the length of each stick in order.
Output
Sample Input
4
5 4 3 6
4
6 5 4 3
Sample Output
1
-1
Source
#include <iostream>
#include <cstdio>
#include <cmath> using namespace std; const int MAXN = ; int n;
int a[MAXN];
int st1[MAXN][], st2[MAXN][];//分别是最小RMQ和最大RMQ
int d[]; void rmqinit() {
for (int i=; i<=n; i++) st1[i][] = st2[i][] = i;
int k = int(log(double(n)) / log(2.0)) + ;
for (int j=; j<k; j++) {
for (int i=; i<n; i++) {
st1[i][j] = st1[i][j-];
st2[i][j] = st2[i][j-];
int part = i + ( << (j-));
if (part >= n) break;
if (a[ st1[i][j-] ] > a[ st1[part][j-] ]) st1[i][j] = st1[part][j-];
if (a[ st2[i][j-] ] < a[ st2[part][j-] ]) st2[i][j] = st2[part][j-];
}
}
} int rmqmin(int l, int r) {
int k = int( log(double(r-l+)) / log(2.0) );
int part = r - (<<k) + ;
if (a[ st1[l][k] ] < a[ st1[part][k] ]) return st1[l][k];
return st1[part][k];
} int rmqmax(int l, int r) {
int k = int( log(double(r-l+)) / log(2.0) );
int part = r - (<<k) + ;
if (a[ st2[l][k] ] > a[ st2[part][k] ]) return st2[l][k];
return st2[part][k];
} int bin_search(int a) {
int l = a, r = n - ;
while (l < r) {
int mid = ((l+r)>>) + ((l+r)&);
if (a == rmqmin(a, mid)) { //如果l~mid范围内的最小元素是l,说明l~mid都大于元素l
l = mid;
}
else {
r = mid - ;
}
}
return l;
} int work() {
int res = -;
for (int i=; i<n; i++) {
int r = rmqmax(i, bin_search(i));
res = max(res, r-i);
}
if (res == ) return -;
return res;
} int main()
{
while (scanf("%d", &n) != EOF) {
for (int i=; i<n; i++) {
scanf("%d", a+i);
} rmqinit();
printf("%d\n", work());
} //cout << "Hello world!" << endl;
return ;
}
RMQ (Range Minimal Query) 问题 ,稀疏表 ST的更多相关文章
- 算法学习 - ST表 - 稀疏表 - 解决RMQ问题
2017-08-26 21:44:45 writer:pprp RMQ问题就是区间最大最小值查询问题: 这个SparseTable算法构造一个表,F[i][j] 表示 区间[i, i + 2 ^ j ...
- 基于稀疏表(Sparse Table)的RMQ(区间最值问题)
在RMQ的其他实现方法中,有一种叫做ST的算法比较常见. [构建] dp[i][j]表示的是从i起连续的2j个数xi,xi+1,xi+2,...xi+2j-1( 区间为[i,i+2j-1] )的最值. ...
- 动态规划——稀疏表求解RMQ问题
RMQ (Range Minimum/Maximum Query)问题,即区间最值查询问题,是求解序列中的某一段的最值的问题.如果只需要询问一次,那遍历枚举(复杂度O(n))就是最方便且高效的方法,但 ...
- ST (Sparse Table:稀疏表)算法
1541:[例 1]数列区间最大值 时间限制: 1000 ms 内存限制: 524288 KB提交数: 600 通过数: 207 [题目描述] 输入一串数字,给你 MM 个询问 ...
- 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 ...
- Range Minimum Query and Lowest Common Ancestor
作者:danielp 出处:http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAnc ...
- AOJ DSL_2_E Range Add Query (RAQ)
Range Add Query 数列 A = {a1,a2,...,an} に対し.次の2つの操作を行うプログラムを作成せよ. add(s,t,x): as,as+1,...,at にxを加算する. ...
- AOJ DSL_2_D Range Update Query (RUQ)
Range Update Query 数列 A = {a0,a1 ,...,an−1} に対し.次の2つの操作を行うプログラムを作成せよ. update(s,t,x): as,as+1,...,at ...
- [LeetCode] Range Sum Query 2D - Mutable 二维区域和检索 - 可变
Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...
随机推荐
- commandname+commandargument
(一) CommandName:其实可以设置成一种动作,比如Select,Update,Delete,等操作.就是说CommandName是确定他到底引发的是哪一事件,如果CommandName的名字 ...
- 用php随机生成福彩双色球号码的2种方法
不瞒您说,俺也是个双色球爱好者,经常买,但迟迟没有中过一等奖,哈哈.这里为大家介绍用php随机生成福彩双色球号码的二种方法,供朋友们学习参考.新的一年,祝大家中大奖,发大财. 方法一 复制代码代码如下 ...
- PHP调用java的class
PHP调用java的class 转:http://hi.baidu.com/lei0827/blog/item/28439a4e923234ced1c86a18.html PHP调用java的cl ...
- 什么是blob,mysql blob大小配置介绍
什么是blob,mysql blob大小配置介绍 作者: 字体:[增加 减小] 类型:转载 BLOB (binary large object),二进制大对象,是一个可以存储二进制文件的容器.在计 ...
- empty($w)
<?php $w = ''; var_dump(empty($w)); $w = ' '; var_dump(empty($w)); $w = 0; var_dump(empty($w)); v ...
- Android 加入一个动作按钮
在XML中声明一个动作按钮 所有的动作按钮和其他的可以利用的items都定义在menu资源文件夹中的XML文件中.为了增加一个动作按钮到工具栏,需要在工程 /res/menu/ 目录下面创建一个新的X ...
- [Virtualization][SDN] VXLAN到底是什么 [转]
写在转发之前: 几个月以前,在北大机房和燕园大厦直接拉了一根光钎.两端彼此为校园内公网IP.为了方便连接彼此机房,我做个一个VPN server在燕园的边界,北大机房使用client拨回.两个物理机房 ...
- 读书笔记——《图解TCP/IP》(4/4)
经典摘抄 第八章 应用层协议概要 1.应用协议是为了实现某种应用而设计和创造的协议. 2.TCP/IP的应用层包含了管理通信连接的会话层功能.转换数据格式的表示层功能,还包括与对端主机交互的应用层功能 ...
- linux configure
Linux环境下的软件安装,并不是一件容易的事情;如果通过源代码编译后在安装,当然事情就更为复杂一些;现在安装各种软件的教程都非常普遍;但万变不离其中,对基础知识的扎实掌握,安装各种软件的问题就迎刃而 ...
- Lazarus for Raspbian安装
春节前看到树莓派 2代开始销售,第一时间在淘宝下单购买,无奈春节期间放假,要到3月份才可能收到,只能用QEMU模拟器先熟悉树莓系统.对从turbo Pascal开始的人来讲,如果能在树莓系统使用Pas ...