<<  < 2013 - >  >>
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30




今天在NYOJ上看到一个编号为119的题目(由于本人初学线段树,所以急切地找有关线段树的题目)

题目大意是告诉你一个数字序列的每一个数字,对于每一条询问x,y,写出从第x个数到第y个数的最大值与最小值的差。

首先一看,我一心认为这是一个线段是的题目,于是我用链表写了一个线段树,交上去以后才发现悲剧了,TML,难道线段树还不够强大吗?

后来我看了最有解答后才发现,对于处理区间最值,尤其是当询问的次数较多的时候,有一个和线段树的强度差不多,甚至更强的算法——RMQ算法。

算法的大意是把整个区间看成是由1,2,,4,8,16等长度组成的区间,这样记录最值,然后由前面i-1可以递推出i,继而迅速得出最大值与最小值。

具体代码如下:

#i nclude <iostream>
#i nclude <stdio.h>
#i nclude <cmath>
using namespace std;
int fmaxm[20][101010],fminm[20][101010],n,m,i,j,l,k,now,maxm,minm;
int main()
{
    scanf("%d %d",&n,&m);
    for ( i=1; i<=n; i++) { scanf("%d",&fmaxm[0][i]); fminm[0][i]=fmaxm[0][i]; }
    for ( i=1; i<20; i++)
    {
        for (j=1; j+(1<<i)<=n; j++)
          {
              fmaxm[i][j]=max(fmaxm[i-1][j],fmaxm[i-1][j+(1<<(i-1))]);
              fminm[i][j]=min(fminm[i-1][j],fminm[i-1][j+(1<<(i-1))]);
          }
    }
    int x,y;
    for (l=1; l<=m; l++)
    {
        scanf("%d %d",&x,&y);
        if (x==y) { printf("0\n"); continue;}
        k=y-x;
        k=int ( (log(k)+0.1)/log(2) ); if (y-x==8) k=3;
        now=x; maxm=fmaxm[0][y]; minm=fminm[0][y];
        while (k>=0)
        {
            while (now+(1<<k)<=y)
            {
                if (fmaxm[k][now]>maxm) maxm=fmaxm[k][now];
                if (fminm[k][now]<minm) minm=fminm[k][now];
                now+=1<<k;
            }
            k-=1;
        }
        printf("%d\n",maxm-minm);
    }
    return 0;
}

有图片为证(此题的时限是2000MS)

 

  • 标签:RMQ 
  • 发表评论:
    天涯博客欢迎您!