zoukankan      html  css  js  c++  java
  • POJ 3264 Balanced Lineup RMQ ST算法

    题意:有n头牛,编号从1到n,每头牛的身高已知。现有q次询问,每次询问给出a,b两个数。要求给出编号在a与b之间牛身高的最大值与最小值之差。

    思路:标准的RMQ问题。

    RMQ问题是求给定区间内的最值问题。当询问量巨大时,最朴素算法必然超时。解决RMQ比较优秀的算法有ST算法。其预处理时间复杂度为O(nlogn),询问的时间复杂度为O(1)。

    ST的思想如下:

    假设num数组中的数据从第0位开始存储。

    用两个二维数组tmax,tmin分别求区间最大与最小值。ST的关键是数组区间的分割。tmax和tmin的下标是一致的,暂且拿tmax举例。

    预处理:

    预处理阶段运用的是DP的思想。tmax[i][j]内的值为区间[i, i + 2^j - 1]内的最大值。可以方便地理解为:第一个下标i为区间的开始位置,第二个坐标j表示区间的长度(只不过长度为指数形式)。如tmax[2][1]表示的是区间[2, 3]的最大值,tmax[2][2]表示的是区间[2, 5]的最大值。

    而区间[i, i + 2^j - 1]可拆成[i, i + 2^(j - 1) - 1]和[i + 2^(j - 1), i + 2^j - 1]两个子区间。因此要计算tmax[i][j]的值,则有tmax[i][j] = max(tmax[i][j-1], tmax[i+2^(j-1)][j-1])。而所有递推的最初值tmax[i][0] = num[i]。对于tmin数组,下标的表示规则是相同的。

    查询:

    预处理进行完之后,可以进行查询。查询的复杂度为O(1)。

    假设要查询区间[i, j]内的最大值。

    首先第一步,先计算出一个整数k,k为满足表达式i + 2^k - 1 <= j 的最大整数。

    然后将区间[i, j]分成两个部分重叠的子区间:[i, i + 2^k - 1]与[j - 2^k + 1, j]。

    而tmax[i][k] 与tmax[j-2^k+1][k]中在预处理阶段便已计算出了结果,此时只需要输出两者中的较大者即可。

    其他细节请看代码。

     1 #include<stdio.h>
     2 #include<math.h>
     3 #include<algorithm>
     4 #define maxn 50020
     5 using namespace std;
     6 
     7 int cow[maxn], tmax[maxn][33], tmin[maxn][33];
     8 void st(int n)
     9 {
    10     int k = (int)(log((double)n) / log(2.0));
    11     for (int i = 0; i < n; i++)
    12         tmin[i][0] = tmax[i][0] = cow[i];//递推的初值
    13     for (int j = 1; j <= k; j++)
    14         for (int i = 0; i + (1 << j) - 1 < n; i++)
    15         {
    16             int m = i + (1 << (j - 1));//求出中间值
    17             tmax[i][j] = max(tmax[i][j-1], tmax[m][j-1]);
    18             tmin[i][j] = min(tmin[i][j-1], tmin[m][j-1]);
    19         }
    20 }
    21 //查询i和j之间的最值,注意i是从0开始的
    22 void rmq(int i, int j)
    23 {
    24     int k = (int)(log(double(j - i + 1)) / log(2.0));
    25     int t1 = max(tmax[i][k], tmax[j-(1<<k)+1][k]);
    26     int t2 = min(tmin[i][k], tmin[j-(1<<k)+1][k]);
    27     printf("%d
    ",t1 - t2);
    28 }
    29 int main()
    30 {
    31     int n, q;
    32     //freopen("data.in", "r", stdin);
    33     scanf("%d%d",&n,&q);
    34     for (int i = 0; i < n; i++) scanf("%d",&cow[i]);
    35     st(n);
    36     while (q--)
    37     {
    38         int a, b;
    39         scanf("%d%d",&a,&b);
    40         rmq(a - 1, b - 1);//st算法从第0位开始,因此需要减一
    41     }
    42     return 0;
    43 }
  • 相关阅读:
    uni-app中的数值监控方式及函数的封装和引用方式
    uni-app引入阿里矢量图在移动端不显示的问题
    前端登录页点击获取验证码的实现
    app每次更新版本时调用js代码提示用户下载更新
    @Dependson注解与@ConditionalOnBean注解的区别
    navicat for mysql 12中文破解版(安装+破解)--亲测可用
    Kubernetes 常用命令
    MySQL MERGE存储引擎
    MySQL中MyISAM与InnoDB区别
    什么是事务?什么是事务日志以及用途?
  • 原文地址:https://www.cnblogs.com/fenshen371/p/3222514.html
Copyright © 2011-2022 走看看