数据结构专题小结:RMQ问题

RMQ问题

范围最小值问题(Range Minimum Query)是指:给定一个n个元素的数组A[1],A[2]...A[n]。设计一个数据结构,支持查询操作Query(L,R):计算min{A[L],A[L+1]...A[R]}。

该问题在实践中常用Tarjan的Sparse-Table算法。它的预处理时间是O(N*logN),但查询只需要O(1),而且常数非常小。最重要的是,这个算法非常好写,而且不易写错。

(1)原理:该算法利用了分治法的思想,令d(i,j)表示从i开始的,长度为2^j的一段元素中的最小值。则不难发现如下公式成立:

d(i,j)=min{d(i,j-1),d(i+2^(j-1),j-1)}

注意到2^i≤n,因此d数组的元素个数不超过n*logn个,而每一项都能在常数时间内算完。因此总的时间复杂度是O(N*logN)。

#define N 1000
int d[N][N];
vector<int>A;
#define INF 100000000
void RMQ_init(const vector<int>&A)//初始化操作,所有元素放到vector中
{
	for (int i = 0; i < N;i++)
	for (int j = 0; j < N; j++)
		d[i][j] = INF;
	int n = A.size();
	for (int i = 0; i < n; i++)
		d[i][0] = A[i];
	for (int j = 1; (1 << j) <= n;j++)
	for (int i = 0; i + (1 << j) - 1 < n; i++)
		d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][i - 1]);
}

查询操作也很简单,令k为满足2^k≤R-L+1的最大整数,则以L开头,以R结尾的两个长度为2^k的区间合起来即覆盖了查询区间[L,R]。由于是最小值,有些元素重复考虑了也没有关系。

int RMQ(int L, int R)
{
	int k = 0;
	while ((1 << (k + 1)) <= R - L + 1)k++;
	return min(d[L][R], d[R - (1 << k) + 1][k]);
}
时间: 2024-10-14 11:28:33

数据结构专题小结:RMQ问题的相关文章

数据结构专题小结:并查集

并查集 并查集的作用是快速判断两个数是否属于同一类的数据结构,不过除此之外,它还可以实现合并u和v所在的组.下面给出并查集的一系列操作的实现. #define N 100 int par[N]; int rnk[N]; void init(int n)//初始化n个元素 { for (int i = 0; i < n; i++) { par[i] = i; rnk[i] = 0; } } int find(int x)//查询树的根 { return par[x] == x ? x : par[

线段树--数据结构专题学习

这两周是数据结构专题的学习,,被专题的题目虐得死去活来== 线段树:简单的说就是把[1,n]的区间二分,[1,(1+n)/2]左子树,[(1+n)/2+1,n]右子树 就这样一直分下去,直到都是[x,x]这样的区间.这样就构成了一颗树了^-^ 有这样一棵树,我们就可以在节点中储存区间的和啊,区间内的最大值啊,最小值等等..这就是线段树的附加信息了,也是题目中的重点.. 我们可以用一个数组(长度为k)储存原区间的初始值,然后根据这个建树,所以这个树的节点数最多为4*K: 对于每个节点i,其左子树为

2014 UESTC暑前集训数据结构专题解题报告

A.Islands 这种联通块的问题一看就知道是并查集的思想. 做法:从高水位到低水位依序进行操作,这样每次都有新的块浮出水面,可以在前面的基础上进行合并集合的操作.给每个位置分配一个数字,方便合并集合.同时将这些数字也排一个序,降低枚举的复杂度.合并集合时向四周查询浮出水面但是没有合并到同一集合的点进行合并. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath&

搜索专题小结及例题:POJ2251&amp;POJ1426&amp;POJ3087&amp;POJ2488

图的遍历也称为搜索,就是从图中某个顶点出发,沿着一些边遍历图中所有的顶点,且每个顶点仅被访问一次,遍历可采取两种不同的方式:深度优先搜索(DFS)和广度优先搜索(BFS). 1.DFS算法思想` 从顶点v出发深度遍历图G的算法 ① 访问v0顶点,置vis[v0]=1,搜索v0未被访问的邻接点w,若存在邻接点w,则dfs(w),直到到达所有邻接点都被访问过的顶点u为止,接着退回一步,看是否还有其他没有被访问的邻接点.如果有,则访问此顶点,进行前述类似的访问,如果没有,就在退回一步进行搜索,重复上述

数据结构专题——队列的应用 A1056.Mice and Rice ( 25)

#include <bits/stdc++.h> #include<math.h> #include <string> using namespace std; const int maxn = 1010; struct mouse{ int weight;//质量 int R;//排名 }mouse[maxn]; int main(){ int np,ng,order; scanf("%d%d",&np,&ng); for(int

2017.8.11 数据结构课小结

今天讲了并查集.堆和Hash表,并讲了几道比较难的题. 例1. 分析:其实这道题我们用一颗很普通的线段树,维护区间最大值就好了,因为最大值开方后还是最大值嘛. 但是开方有一个比较重要的性质:一个10^7的数大概7,8次开方就是1了,以后不管怎么开方都是1,我们能不能不考虑这部分1呢?也就是说,我们能不能用一个数据结构跳过1呢?可以想到用双向链表,但是这样能不能做到线性呢?回想一下链表的操作,我们的第一步是要"找到"链表,也就是说我们要找到当前区间的第一个不是1的表头,这样其实很容易卡掉

图论专题小结:网络流算法之ISAP算法

ISAP算法 ISAP(Improved Shortest Augument Path)算法是改进版的SAP算法,如果对效率要求很高的时候,可以用该算法. (1)概述:算法基于这样的一个事实:每次增广之后,任意结点到汇点(在残余网络中)的最短距离都不会减小.这样,我们可以利用d[i[表示结点i到汇点的距离的下界.然后再增广过程当中不断地修改这个下界.增广的时候和Dinic算法类似,只允许沿着d[i]==d[j]+1的弧(i,j)走. 不难证明,d[i[满足两个条件:(1)d[t]=0;(2)对任

数据结构专题——队列

一.队列(queue)(可与栈对比进行学习) 思想:队列实现的是一种先进先出(first-in,first-out,FIFO)策略.(<算法导论>) 定义:队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表(具有线性关系/前驱后继关系).(<大话数据结构>) 术语: 队列的两端:队头(head):进行删除操作的一端.队尾(tail):进行插入操作的一端. 操作:队列的插入操作(insert):入队(enqueue). 队列的删除操作(delete):出队(dequeue

OpenJudge_cdqz 数据结构版块小结

题目整理 Challenge 0  随机线性存储表-easy Challenge 1  链表数组-easy Challenge 2  可持久化Treap的可持久化运用-hard Challenge 3  线段树-easy Challenge 4  线段树-medium Challenge 5  线段树加lazy标记-medium Challenge 6  分块-hard Challenge 7  离散化或stl_map-easy Challenge 8  平衡树-easy Challenge 9