您的位置:首页 > 其它

poj 3246 RMQ模板

2016-05-16 21:15 351 查看
[align=center]Balanced Lineup[/align]

Time Limit: 5000MSMemory Limit: 65536K
Total Submissions: 43998Accepted: 20647
Case Time Limit: 2000MS
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 andQ.

Lines 2..N+1: Line i+1 contains a single integer that is the height of cowi

Lines N+2..N+Q+1: Two integers A and B (1 ≤A ≤B ≤N), representing the range of cows fromA toB 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

题意:多组测试数据,每组第一行输入n和m,分别表示n个数和m次查询,输出每次查询区间内的最大值和最小值的差。

解题思路:这是我的第一道RMQ题,表示做之前很是迷茫,不理解其中的ST算法,不过在学习了RMQ后,才发现这是个比较简单的算法,其中最难理解的可能就是ST了吧。

这个题是一道最基础的RMQ模板题,直接套用模板就可以过,也可以使用线段树,不过时间效率可能比较低,线段树讲解详见(线段树线段树延迟标记精讲)。RMQ算法的最大优点就是经过
O(nlogn)的预处理,是每次查询能够达到O(1),大大提高了查询效率。

下面详细介绍一下RMQ算法:

RMQ算法

对于该问题,最容易想到的解决方案是遍历,复杂度是O(n)。但当数据量非常大且查询很频繁时,该算法无法在有效的时间内查询出正解。所以又引进了一种比较高效的在线算法(ST算法)解决这个问题。所谓在线算法,是指用户每输入一个查询便马上处理一个查询。该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

ST算法实质就是一个DP

例如数列A:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

35 13 65 99 88 75 64 51 42 55 66 83 12 44 65 12

f[i,j]表示的从i开始的2^j个数里面的最值。以最大值来举例说明。

f[1,0]就是从1开始的2^0个数里的最大值,既35,所以f[1,0]=35;

f[1,1]就是从1开始的2^1个数里的最大值,既35,13,所以f[1,1]=35;

f[1,2]就是从1开始的2^2个数里的最大值,既35,13,65,99,所以f[1,2]=99;

f[2,0]就是从2开始的2^0个数里的最大值,既13,所以f[2,0]=13;

f[2,3]就是从2开始的2^3个数里的最大值,既13,65,99,88,75,64,51,42,所以f[2,0]=99;

以此类推......

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

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

求i至其后的2^j个数中的最大值,既f[i][j]的最大值,利用二分的思想,把区间i~j分成两半分别来求两个区间的最大值;就是把j分成两个长度为2^(j-1)的区间,从 i~i+2^(j-1)-1为一段,i+2^(j-1)到 i+2^j-1为一段,长度都为2^(j-1),

比如f[5,3],包含的值有88 75 64 51 42 55 66 83。

求它的最大值,就是[88 75 64 51],[42 55 66 83]的最大值中较大的。即f[5,2]和 f[9,2]。

状态转移方程既是 f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1]);

代码如下:
//次处求出2^p<=n时的p,即从i开始往后的2^j的数
int p=int(log((double)n)/log(2.0));
for(j=1;j<=p;j++)
for(i=1;i+(1<<j)-1<=n;i++)
{
sa[i][j]=max(sa[i][j-1],sa[i+(1<<(j-1))][j-1]);
si[i][j]=min(si[i][j-1],si[i+(1<<(j-1))][j-1]);
}


然后是查询,假如我们需要查询的区间为f(a,b),那么我们需要找到覆盖这个闭区间(左边界取a,右边界取b)的最小幂,p=int(log((double)(b-a+1))/log(2.0));,按照之前的状态方程需要把该区间分成等长的两部分,既max(f[a][p],f[b-2^p+1][p]),得到的最大值就是所求区间的最大值,时间复杂度为惊人的O(1)。
具体代码:
#include <stdio.h>
#include <math.h>
#include <algorithm>
#define N 50005
using namespace std;
int sa
[30];
int si
[30];
void RMQ_Init(int n)
{
int e,i,j;
for(i=1;i<=n;i++)
{
scanf("%d",&e);
sa[i][0]=si[i][0]=e;//DP初始化呢
}
//次处求出2^p<=n时的p,即从i开始往后的2^j的数 int p=int(log((double)n)/log(2.0)); for(j=1;j<=p;j++) for(i=1;i+(1<<j)-1<=n;i++) { sa[i][j]=max(sa[i][j-1],sa[i+(1<<(j-1))][j-1]); si[i][j]=min(si[i][j-1],si[i+(1<<(j-1))][j-1]); }
}
int RMQ(int a,int b)
{
int p=int(log((double)(b-a+1))/log(2.0));
int maxh=max(sa[a][p],sa[b-(1<<p)+1][p]);
int minh=min(si[a][p],si[b-(1<<p)+1][p]);
return maxh-minh;
}
int main()
{
int n,m,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
RMQ_Init(n);
while(m--)
{
scanf("%d%d",&a,&b);
if(a==b)
printf("0\n");
else
printf("%d\n",RMQ(a,b));
}
}
return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: