zoukankan      html  css  js  c++  java
  • pku3264Balanced Lineup RMQST解法

    Notations
    Range Minimum Query(RMQ)
          给定数组A[0, N-1]找出给定的两个索引间的最小值的位置。


    Trivial algorithms for RMQ

           对每一对索引(i, j),将RMQA(i, j)存储在M[0, N-1][0, N-1]表中。普通的计算将得到一个<O(N3), O(1)> 复杂度的算法。尽管如此,通过使用一个简单的动态规划方法,我们可以将复杂度降低到<O(N2), O(1)>。预处理的函数和下面差不多:
    这个普通的算法相当的慢并且使用 O(N2)的空间,对于大数据它是无法工作的。

    An <O(N), O(sqrt(N))> solution

           现在让我们看看怎样计算RMQA(i, j)。想法是遍历所有在区间中的sqrt(N)段的最小值,并且和区间相交的前半和后半部分。为了计算上图中的RMQA(2,7),我们应该比较A[2]A[M[1]]A[6] 和A[7],并且获得最小值的位置。可以很容易的看出这个算法每一次查询不会超过3 * sqrt(N)次操作。

          这个方法最大的有点是能够快速的编码(对于TopCoder类型的比赛),并且你可以把它改成问题的动态版本(你可以在查询中间改变元素)。

    Sparse Table (ST) algorithm    

         一个更好的方法预处理RMQ 是对2k 的长度的子数组进行动态规划。我们将使用数组M[0, N-1][0, logN]进行保存,其中M[i][j]是以i 开始,长度为 2j的子数组的最小值的索引。下面是一个例子

    为了计算M[i][j]我们必须找到前半段区间和后半段区间的最小值。很明显小的片段有这2j - 1 长度,因此递归如下:



    一旦我们预处理了这些值,让我们看看怎样使用它们去计算RMQA(i, j)。思路是选择两个能够完全覆盖区间[i..j]的块并且找到它们之间的最小值。设k = [log(j - i + 1)].。为了计算RMQA(i, j) 我们可以使用下面的公式

    #include<iostream>

    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<stdio.h>
    #include<stdlib.h>
    using namespace std;
    #define Min(a,b) (((A[a]) <(A[b])) ? (a) : (b))
    #define Max(a,b) (((A[a]) >(A[b])) ? (a) : (b))
    #define maxn 50005
    int A[maxn];///////////数据
    int Mmin[maxn][20];////M[i][j]是以i 开始,长度为 2j的子数组的最小值的索引。 j<log2(maxn)+1;
    int Mmax[maxn][20];////M[i][j]是以i 开始,长度为 2j的子数组的最大值的索引。 j<log2(maxn)+1;
    void process(int n)//////预处理函数
    {
         int i,j,k;
         int m=(int)floor(log((double)n)/log(2.0));
         for(i=0;i<=n;i++)
         Mmin[i][0]=Mmax[i][0]=i;
         for(j=1;1<<j<=n;j++)///// 2^j<=n
         {
            k=1<<j;
            for(i=1;(i+k-1)<=n;i++)///////i+长度k-1<=总长;        
            {
                Mmin[i][j]=Min(Mmin[i][j-1],Mmin[i+(1<<(j-1))][j-1]);///////////////#define Min();
                Mmax[i][j]=Max(Mmax[i][j-1],Mmax[i+(1<<(j-1))][j-1]);///////////////#define Max();
            }                
         }
    }
    int query(int a,int b)
    {
        int k=(int)floor(log((double)(b-a+1))/log(2.0));///////log2(b-a+1);
        int p=1<<k;
        int mmin=min(A[Mmin[a][k]],A[Mmin[b-p+1][k]]);
        int mmax=max(A[Mmax[a][k]],A[Mmax[b-p+1][k]]);
        return mmax-mmin;  
    }
    int main()
    {
        int n,q;
        int a,b;
        int i,j;
        while(scanf("%d%d",&n,&q)!=EOF)
        {
             for(i=1;i<=n;i++)
             scanf("%d",&A[i]);
             process(n);
             while(q--)
             {        
                  scanf("%d%d",&a,&b);
                  printf("%d\n",query(a,b));
             }
        }
        return 0;
    }

    声明:代码上面的RMQ分析转自:一切随心http://www.cnblogs.com/drizzlecrj/archive/2007/10/23/933472.html

    一个比较有趣的点子是把向量分割成sqrt(N)大小的段。我们将在M[0,sqrt(N)-1]为每一个段保存最小值的位置。
    M可以很容易的在O(N)时间内预处理。下面是一个例子:

  • 相关阅读:
    【BZOJ1006】神奇的国度(弦图)
    弦图
    【BZOJ2946】公共串(后缀数组)
    【POJ1743】Musical Theme(后缀数组)
    JAVA和Tomcat运维整理
    linux shell 之if-------用if做判断
    Linux curl命令详解
    Intel HEX文件解析
    Linux bridge-utils tunctl 使用
    怎样查询锁表的SQL
  • 原文地址:https://www.cnblogs.com/sachin/p/2691523.html
Copyright © 2011-2022 走看看