RMQ——快速求区间内最值

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… N+ Q+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

题目链接

http://poj.org/problem?id=3264

题目大意

有N头牛,已知他们的高度,问第A到B头牛中最高的和最低的差是多少。
第一行,数N,Q表示告诉你有N头牛,Q次询问。接下来N行表示每头牛的高度。接下来Q行表示每次询问的A和B。

数据范围

(1 ≤ N ≤ 50,000)
(1 ≤ Q ≤ 200,000)
(1 ≤ height ≤ 1,000,000)
(1 ≤ A ≤ B ≤ N)

解题思路

首先想到的就是最普通的做法,直接每次找一遍最值求差就行了。
代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int a[500005];
int main(){
int n,q,l,r,Max = 0,Min = 2000000;
scanf("%d %d",&n,&q);
for(int i = 1; i <= n;i++){
	scanf("%d",&a[i]);
	}
for(int i = 1;i <= q;i++){
	scanf("%d %d",&l,&r);
	for(int j = l;j <= r;j++){
		Max =  max(Max,a[j]);
		Min = min(Min,a[j]);
		}
		printf("%d\n",Max - Min);
		Max = 0;
		Min = 2000000;
	}
	return 0;
}

样例是过了,但是一交上去就time limit ,很显然这个题目要用算法来优化一下。然后问同学说要用RMQ,网上找了一下,还有说用什么线段树啥的,不是很懂,但是感觉思路很有趣,帮助了我对RMQ的理解。
这里放几篇感觉写的不错的线段树文章

线段树从零开始 By 岩之痕

https://blog.csdn.net/zearot/article/details/52280189

线段树 糖炒栗之
https://blog.csdn.net/qq_39826163/article/details/81436440

感兴趣的同学可以自己学习

然后我查了RMQ的文章,了解了一下,关键在于
dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1])
这样一个想法。即利用动态规划,其中i表示第i个数,j表示从i后的第2^j个数。
注意
i后的第2的j次幂个数,要把i作为第一个数,数进去

将存入每头牛身高数据的数组a[N] 赋值给dmax[i][0],进行一个初始化操作,利用将每段询问区间拆分为两段的想法,求局部最值,再整合。

网上看到的一个不错的RMQ模板

RMQ学习 Soar-

https://blog.csdn.net/sgh666666/article/details/80448284

他的括号打错了,我修改一下,再粘贴出来

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=50000+5;
 
int dmax[MAXN][20];
int dmin[MAXN][20];
 
void initmax(int n,int d[])//初始化最大值查询
{
    for(int i=1; i<=n; i++)
        dmax[i][0]=d[i];
    for(int j=1 ; (1<<j)<=n ; j++)
        for(int i=1; i+(1<<j)-1 <=n; i++)
            dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
}
int getmax(int L,int R)//查询最大值
{
    int k=0;
    while((1<<(k+1))<=R-L+1)k++;
    return max(dmax[L][k] , dmax[R-(1<<k)+1][k]);
}
 
void initmin(int n,int d[])//初始化最小值查询
{
    for(int i=1; i<=n; i++)
        dmin[i][0]=d[i];
    for(int j=1; (1<<j)<=n; j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dmin[i][j]= min( dmin[i][j-1],dmin[i+(1<<(j-1))][j-1] );
}
int getmin(int L,int R)//查询最小值
{
    int k=0;
    while( (1<<(k+1)) <=R-L+1)k++;
    return min(dmin[L][k],dmin[R-(1<<k)+1][k]);
}

解决代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=50000+5;
 
int dmax[MAXN][20];
int dmin[MAXN][20];
 
void initmax(int n,int d[])//初始化最大值查询
{
    for(int i=1; i<=n; i++)
        dmax[i][0]=d[i];
    for(int j=1 ; (1<<j)<=n ; j++)
        for(int i=1; i+(1<<j)-1 <=n; i++)
            dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
}
int getmax(int L,int R)//查询最大值
{
    int k=0;
    while((1<<(k+1))<=R-L+1)k++;
    return max(dmax[L][k] , dmax[R-(1<<k)+1][k]);
}
 
void initmin(int n,int d[])//初始化最小值查询
{
    for(int i=1; i<=n; i++)
        dmin[i][0]=d[i];
    for(int j=1; (1<<j)<=n; j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dmin[i][j]= min( dmin[i][j-1],dmin[i+(1<<(j-1))][j-1] );
}
int getmin(int L,int R)//查询最小值
{
    int k=0;
    while( (1<<(k+1)) <=R-L+1)k++;
    return min(dmin[L][k],dmin[R-(1<<k)+1][k]);
}
int main(){
	int n,q,l,r,ans;
	int a[MAXN];
	scanf("%d %d",&n,&q);
	for(int i = 1;i <= n;i++){
		scanf("%d",&a[i]);
	}
	initmax(n,a);
	initmin(n,a);
	for(int i = 1;i <= q;i++){
		scanf("%d %d",&l,&r);
		ans = getmax(l,r) - getmin(l,r);
		printf("%d\n",ans);
		}
	return 0;
	}

猜你喜欢

转载自blog.csdn.net/FOWng_lp/article/details/86613589
今日推荐