zoukankan      html  css  js  c++  java
  • 浅谈ST表

    咱们先来前情引导一下

      假如你的面前有一个区间【1,6,2,8,7,4,6,3,9,3,0】请问,这个区间的最大值是森魔?最小值是森魔?千万别说你不知道

    答案显然:max值是9,min值是0。这种问题往往被我们RMQ ( Range Minimum / Maximum Query ) 问题,是指:对于长度为 n 的数列 A,回答若干询问 RMQ (A , i , j ) ( i , j ≤ n),返回数列A中下标在 i , j 里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。

    现在把自己想象成一个什么也不懂的孩子,假如你一眼看不出来这个区间的最值,你会怎样寻找它?

    第一种想法,从左到右做两个指针i,j,从左到右暴力枚举。遇见大的(举例)就更新,不然就跳过。这种暴力算法的时间复杂度是O(n)-O(n)的

    第二种想法,利用线段树(一种强大的数据结构)进行区间查询,其时间复杂度为O(n)-O(logn)。

    但是,如果还没有学过线段树算法,也不想用暴力。那么我们的ST表就要登场啦。

    算法介绍

    ST表这个算法,其功能很单调,主要就是用于求解区间RMQ问题(但是请注意ST表只能用于静态查询,如果题目中是动态的RMQ还是推荐线段树),它可以做到O(nlogn)的预处理,O(1)的查询,可以说很强大了。

    ST表的思想是基于动态规划的,下面以求一段区间的最大值为例子。

    我们用f[i][j]表示以i为起点,连续的2^j个数中的最大值(左闭右开),f[1][3]表示从1到8的最大值。

    (1)第一步:预处理出f数组

    首先,由于20=1。所以f[i][0]就是这个区间的左端,对于f区间,我们不断的对其倍增,使其区间不断扩大,可以得到如下的式子。

    f [ i ][ j ] = max { f [ i ][ j - 1 ] , f [ i + ( 1 << ( j - 1 ) ][ j - 1 ] }(因为我们要求的是最大值嘛每一个j都是由上一个j求过来的,i+(1<<(j-1))就表示两个区间的分开点)

    (2)第二步:把log数组处理出来

    这是在代码中的顺序,在理解时先看第三步

    log[i]=log[i/2]+1(需要向下取整)

    (3)第三步:进行查询

    假设要查询的区间为 [ l , r ],我们用 L 表示区间 [ l , r ] 的长度,即 L = r - l + 1,下面用 k 表示 logL

    其中查询的话,区间长度 L 不一定刚好是 2 的多少次方,又因为 logL 是向下取整,那么 2^k 就有可能小于 L,这样的话,我们就不能直接用 f [ l ][ k ] 来表示答案,不然的话会有遗漏。

    正确的做法是我们就从 l 往右取 2^k 个(即 f [ l ][ k ]),从 r 往左取 2^k 个(即 f [ r - ( 1 << k ) + 1 ][ k ]),这样就能保证区间 [ l , r ] 都被访问到了,重复计算的不用担心,这是计算最值而不是求和

    那么答案 answer = max { f [ l ][ k ] , f [ r - ( 1 << k ) + 1 ][ k ] }

    代码实现

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 const int MAXN=1e6+10;
     8 int maxn[MAXN][21];
     9 int m,n;
    10 int ask(int l,int r)
    11 {
    12     int k=log2(r-l+1);
    13     return max(maxn[l][k],maxn[r-(1<<k)+1][k]);
    14 }
    15 int main()
    16 {
    17     scanf("%d%d",&n,&m);
    18     for(int i=1;i<=n;i++)
    19     {
    20         scanf("%d",&maxn[i][0]);
    21     }
    22     
    23     for(int j=1;j<=21;j++)
    24         for(int i=1;i+(1<<j)-1<=n;i++)
    25         {
    26             maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
    27         } 
    28             
    29     for(int i=1;i<=m;i++)
    30     {
    31         int l,r;
    32         scanf("%d%d",&l,&r);
    33         printf("%d
    ",ask(l,r));
    34     }
    35     return 0;
    36 }

    嗯,就是这样了,谢谢各位!

    多多帮助蒟蒻宣传一下博客哦!

  • 相关阅读:
    在线加密解密
    ctcms Nginx 伪静态
    iTem2 保持连接,解决ssh的"Write failed: Broken pipe"问题
    打开窗口弹出页面
    点击弹窗
    javascript 技巧
    使用Chrome工具来分析页面的绘制状态
    jquery结合JSONP教程—明河谈jquery
    使用 JSONP 实现跨域通信,第 1 部分: 结合 JSONP 和 jQuery 快速构建强大的 mashup
    jsonp详解
  • 原文地址:https://www.cnblogs.com/mo-qian/p/11291330.html
Copyright © 2011-2022 走看看