【RMQ】RMQ算法

【概述】

RMQ(Range Minimum/Maximum Query),即区间最值查询,给定一个长度为n的数列,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。

看到这个,首先想到的应该是遍历,一次时间复杂度为O(n),是一个很好实现的方法,但当数据过大以及查询非常频繁时就会耗时过多,所以应该想一些更高效的算法。。因为求区间最值,所以首先想到的便是线段树,时间复杂度大概为O(nlogn),但今天不提线段树,在这里,还有一种有名的算法便是ST算法,在以O(nlogn)的复杂度内预处理信息然后以O(1)时间处理查询,是一种非常高效的在线算法。

【思路】

我们设f[i, j]以i为起点,长度为2^j的一段区间的最大/最小值,这里以最大值为例

A数列为:3 2 4 5 6 8 1 2 9 7

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;

并且我们可以容易的看出f[i,0]就等于A[i]。(DP的初始值)

这样,DP的状态、初值都已经有了,剩下的就是状态转移方程。

我们把f[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从 i 到i + 2 ^ (j - 1) - 1为一段,i + 2 ^ (j - 1)到i + 2 ^ j - 1为一段(长度都为2 ^ (j - 1))。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。F[i,j]就是这两段各自最大值中的最大值。于是我们得到了状态转移方程f[i, j]=max(f[i,j-1], f[i + 2^(j-1),j-1])。这里直接引用一位大神的讲解。。

于是预处理就好了,接下来就开始查询

例如:

给出l, r,那么我们需要知道一段区间的最小幂,(可以重复,比如查询5,6,7,8,9,我们可以查询5678和6789)。那么我们可以令k = 以2为底,(r - l  + 1)的对数,由于c++里并没有,所以用换底公式k = log(r - l  +1) / log(2.0),那么我们要求的便是max(f[l, k], f[r - (1 << k)  + 1, k])。

一个练手的题目:poj3264

附题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=10244

这道题包含了求区间的最大值与最小值,是个挺好的拿来理解ST算法的题目

Balanced Lineup
Time Limit: 5000MS   Memory Limit: 65536KB   64bit IO Format: %I64d & %I64u

Submit Status

Description

For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

Input

Line 1: Two space-separated integers,  N and  Q
Lines 2..  N+1: Line  i+1 contains a single integer that is the height of cow  i
Lines  N+2..  NQ+1: Two integers  A and  B (1 ≤  A ≤  B ≤  N), representing the range of cows from  A to  B inclusive.

Output

Lines 1..  Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

Sample Input

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

Sample Output

6
3
0

【代码】

#include
#include
#include
using namespace std;

int dpmax[50000 + 5][30 + 5];
int dpmin[50000 + 5][30 + 5];
int a[50000 + 5];
int N, Q;

void RMQ()
{
	for(int i = 1; i <= N; i++) dpmax[i][0] = dpmin[i][0] = a[i];
	for(int j = 1; j <= 30; j++)
	{
		for(int i = 1; i <= N; i++)
		{
			if(i + (1 << j) - 1<= N)
			{
				dpmax[i][j] = max(dpmax[i][j - 1], dpmax[i + (1 << (j - 1))][j - 1]);
				dpmin[i][j] = min(dpmin[i][j - 1], dpmin[i + (1 << (j - 1))][j - 1]);
			}
		}
	}
}
int work(int l, int r)
{
	int k = (int )(log((double)r - l + 1) / log(2.0));
	int tall = max(dpmax[l][k], dpmax[r - (1 << k) + 1][k]);
	int small = min(dpmin[l][k], dpmin[r - (1 << k) + 1][k]);
	return tall - small;
}
int main()
{
	freopen("poj3264.in", "r", stdin);
	freopen("poj3264.out", "w", stdout);
	scanf("%d%d", &N, &Q);
	for(int i = 1; i <= N; i++)
	scanf("%d", &a[i]);
	RMQ();
	for(int i = 1; i <= Q; i++)
	{
		int l, r;
		scanf("%d%d", &l, &r);
		printf("%d\n", work(l, r));
	}
	return 0;
}

额,由于vjudge上面今天提交失败,并不知道是否正确,但能过样例的说。。。目前没发现错误,应该是对的,有错再改吧。。放在这里就当做模板吧。



你可能感兴趣的:(RMQ)