简、易-sachinKung

导航

pku3264Balanced Lineup RMQ--ST解法

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)时间内预处理。下面是一个例子:

posted on 2012-09-18 21:33  sachinKung  阅读(127)  评论(0编辑  收藏  举报