码迷,mamicode.com
首页 > 其他好文 > 详细

提高篇(1):RMQ问题与ST表

时间:2018-07-13 19:06:55      阅读:148      评论:0      收藏:0      [点我收藏+]

标签:数组   printf   name   min   注意   ons   col   sum   john   

RMQ是英文Range Minimum/Maximum Query的缩写,是询问某个区间内的最值,这里讲一种解法:ST算法

ST算法通常用在要多次(10^6级别)询问区间最值的问题中,相比于线段树,它实现更简单,效率更高,但不支持修改,且一般只能维护最值。

ST算法实际上是动规,原理如下:

预处理:

一组数a[1]..a[n],设f[i][j]表示从a[i]到a[i+2^j-1]这个范围中的最值,元素个数为2^j个。

可以分成2部分,即从a[i]至a[i+2^(j-1)-1]与a[i+2^(j-1)]至a[i+2^j-1],所以

f[i][j]也可以分成f[i][j-1]与f[i+2^(j-1)][j-1],整个区间的最大值一定是左右两部分最大值的较大值,

于是可得状态转移方程:f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1]),边界条件为f[i][0]=a[i],这样即可在O(n log(n))的时间内预处理f数组。

询问:

若询问区间[l,r]的最大值,可以先求出最大的x,满足2^x<=r-l+1,那么区间[l,r]=[l,l+2^x-1]U[r-2^x+1,r],两个区间的元素个数都为2^x,

所以[l,r]中的最大值为max(f[l][x],f[r-2^x+1][x]),可以在O(1)内计算出来(对于m次询问,需要O(m)的时间复杂度)。这两个区间虽然有交集,但对最值没有影响,这就是ST算法只使用于区间最值的原因。

总结:

求区间[x,y]的最大值:

k=log2(y-x+1);

ans=max(f[x][k],f[y-2^k+1][k]);

技巧:

因为cmath库中的log2函数效率不高,所以为了提高速度,通常会使用O(N)的递推预处理出1~N这N种区间长度各自对应的k值。

具体地,设log[x]表示log2(x)向下取整,则log[x]=log[x/2]+1。这样总时间复杂度为log(n*log(n)+m+n)。

放一道例题:

平衡阵容(Balanced Lineup)

题目描述

天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. 有一天, John决定让一些牛们玩一场飞盘比赛. 他准备找一群在对列中为置连续的牛来进行比赛. 但是为了避免水平悬殊,牛的身高不应该相差太大. John 准备了Q (1 <= Q <= 180,000) 个可能的牛的选择和所有牛的身高 (1 <= 身高 <= 1,000,000). 他想知道每一组里面最高和最低的牛的身高差别. 注意: 在最大数据上, 输入和输出将占用大部分运行时间. 

输入

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

输出

6

3

0

[参考程序]

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<climits>
 5 #include<cmath>
 6 #include<algorithm>
 7 using namespace std;
 8     
 9 const int N = 50005;
10 int FMAX[N][20], FMIN[N][20];
11     
12 void RMQ(int n)
13 {
14     for(int j = 1; j != 20; ++j)
15     {
16         for(int i = 1; i <= n; ++i)
17         {
18             if(i + (1 << j) - 1 <= n)
19             {
20                 FMAX[i][j] = max(FMAX[i][j - 1], FMAX[i + (1 << (j - 1))][j - 1]);
21                 FMIN[i][j] = min(FMIN[i][j - 1], FMIN[i + (1 << (j - 1))][j - 1]);
22             }
23         }
24     }
25 }
26     
27 int main()
28 {
29     int num, query;
30     int a, b;
31     while(scanf("%d %d", &num, &query) != EOF)
32     {
33         for(int i = 1; i <= num; ++i)
34         {
35             scanf("%d", &FMAX[i][0]);
36             FMIN[i][0] = FMAX[i][0];
37         }
38         RMQ(num);
39         while(query--)
40         {
41             scanf("%d%d", &a, &b);
42             int k = (int)(log(b - a + 1.0) / log(2.0));
43             int maxsum = max(FMAX[a][k], FMAX[b - (1 << k) + 1][k]);
44             int minsum = min(FMIN[a][k], FMIN[b - (1 << k) + 1][k]);
45             printf("%d\n", maxsum - minsum);
46         }
47     }
48     return 0;
49 }

参考书籍:《信息学奥赛一本通·提高篇》

提高篇(1):RMQ问题与ST表

标签:数组   printf   name   min   注意   ons   col   sum   john   

原文地址:https://www.cnblogs.com/jyx-blog/p/9305404.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!