Balanced Lineup(RMQ问题)

本文详细介绍了RMQ区间最值问题的ST链算法,包括其创建和查询操作,以及在E-BalancedLineup问题中的应用。通过ST算法,可以高效地处理区间内最值查询,时间复杂度为预处理O(nlogn)和查询O(1)。实例演示了如何使用代码实现并解决实际场景中的高度差值计算问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RMQ区间最值算法## RMQ区间最值(ST链)

   RMQ ( Range Minimum / Maximum Query ) 问题是指:对于长度为 n 的数列 A,回答若干询问 RMQ (A , i , j ) ( i , j ≤ n),返回数列A中下标在 i , j 里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
   关于RMQ问题,一般情况我们采用ST算法。
   ST算法是一种用于解决RMQ,即区间最值查询)问题的离线算法,类似于线段树和树状数组的,其功能特性差不多,当实现起来的话,显然是ST算法更为简便。
   ST算法的时间复杂度:预处理的是O(nlogn),查询的是O(1);ST表的主体式一个二维数组dp[i][j],第一个状态表示需要需要查询区间的首元素,第二个状态表示从首元素开始向后延伸的长度。
   在查询的时候,你输入的区间不一定总是2的倍数,那么查询的时候便会出现区间重复查询,但是这并不影响最后的结果,也不会影响时间复杂度。

核心算法(ST表)

算法原理

创建操作

   我们设 f[i,j]f[i,j] 表示 从第 i 个元素开始的长度为 2j2j 个元素的最值
   由于元素个数为 2j2j 个,所以从中间平均分成两部分,每一部分的元素个数刚好为 2j−12j−1 个,如下图:
在这里插入图片描述

   整个区间的最大值一定是左右两部分最大值的较大值,满足动态规划的最优子结构状态转移方程:

        f[i,j] = max(f[i,j-1],f[i+2^{j-1},j-1])

   其中边界条件为 f[i,0]=a[i]

查询操作

   对于查询区间 [L,R][L,R],令 xx 为满足 2x<=R−L+12x<=R−L+1 的最大整数,即 x=int(log(R−L+1)/log(2))
   [L,R]=[L,L+2x−1]∪[R−2x+1,R][L,R]=[L,L+2x−1]∪[R−2x+1,R],两个子区间元素个数都是 2x2x 个,如图:
在这里插入图片描述

则有:

      RMQ(L,R) = max(d[L,x],f[R-2^{x}+1,x])

时间复杂度

  • 对于预处理是O(nlogn)
  • 对于查询是O(1)

算法代码

#include<iostream>
using namespace std;
//创建ST表
void REQ_init(vector<int> A) { 
    int n = A.size();
    for (int i = 0; i < n; ++i) f[i][0] = A[i];  //初始化
    for (int j = 1; (1 << j) <= n; ++j) // 枚举长度 2^j ,其中1<<j表示处理范围
         for (int i = 0; i + (1 << j) - 1 < n; ++i) { 
// i+(1<<j)-1<=n 表示所求的范围的最后一个值在原数组范围内
              f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); 
        }
 } 
 //查询操作
 int RMQ(int L, int R) {
    int k = 0; 
    while ((1 << (k + 1)) <= R - L + 1) ++k;  //爬树法思想,代码等价于  int x = int( log(r-l+1)/log(2));
    return max(f[L][k], f[R - (1 << k) + 1][k]);
}

int main(){
    return 0;
}   

注意: 代码 int x = int( log(r-l+1)/log(2)); 的处理速度比经过爬树法处理后的代码速度要快,但是每次调用 std::log 重新计算 log 函数会增加计算量,所以建议进行预处理:

f(x)={Logn[1]= 0Logn[i]= Logn[i2]+1 f(x)=\left\{ \begin{aligned} Logn[1] & = & \ 0 \\ Logn[i] & = & \ Logn[\frac i2]+1 \end{aligned} \right. f(x)=Logn[1]Logn[i]== 0 Logn[2i]+1

题目

E - Balanced Lineup

  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
  iLines 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 Input6 3

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

Sample Output

6
3
0

题意

给定n个奶牛的高度,求区间[s,e]中最高与最低高度的差值。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <vector>
#include <cmath>
#include<string.h>
 
using namespace std;
 
#define LL long long
#define Ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0);
#define mem(a,b) memset(a,b,sizeof(a))

const int MOD = int(1e9)+7;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f3f3f3f3f3f3fLL;
const int  maxn = 50007;
const int N = 22;

int fMin[N][maxn],fMax[N][maxn];
int pow2[N],a[maxn];

void init_rmq(int len){
    int i,j;
    for (i = 0; i < N; ++i) pow2[i] = 1<<i;//pow2[i]表示长度一定小于总长度,左移i位相当于乘i次方
    for (i = 1; i <= len; ++i)
    fMin[0][i] = fMax[0][i] = a[i];//规定区间长度为0的最大和最小值
    for (i = 1; pow2[i] <= len; ++i) {
        for (j = 1; j + pow2[i - 1] <= len; ++j){
            fMax[i][j] = max(fMax[i - 1][j],fMax[i - 1][j + pow2[i - 1]]);
            fMin[i][j] = min(fMin[i - 1][j],fMin[i - 1][j + pow2[i - 1]]);
        }
    }
}
int rmq(int s,int e){
    int k = log(1.0*(e -s + 1))/log(2.0);
    int Max = max(fMax[k][s],fMax[k][e - pow2[k] +1]);
    int Min = min(fMin[k][s],fMin[k][e - pow2[k] +1]);
    return Max - Min;
}
int main(){
    int n,q,i;
    int s,e;
    scanf("%d%d",&n,&q);
    for (i = 1; i <= n; ++i) scanf("%d",&a[i]);

    init_rmq(n);
    while (q--){
        scanf("%d%d",&s,&e);
        printf("%d\n",rmq(s,e));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值