poj 3264 Balanced Lineup(两遍sparse-table算法的rmq问题)

刚搞掉了ping pong那个BIT 照着训练指南乘胜追击学的RMQ问题 这个题我貌似早就见过 一直都不会。刚才有了BIT的思想学了一下 sparse-table算法
还算比较简单吧。
先说说这个算法 就是给出序列 和查询 q i j问你i和j之间的最小值是多少
这个暴力的话要n方 肯定是不行的。但是st算法预处理的时间是nlogn 查询时间基本是o(1)的
我们用一个二维数组d记录
d[i][j]表示的是 以i为起点长度是 2的j次方的这 一段 序列中的最小值。用一点dp的思想 递推得到整个数组
d[i][j]= min(d(i,j-1),d(i+1<<(j-1),j-1))相当于把这个序列分成两段了。然后找最小值
d数组的元素个数不超过nlogn
因为d[i][j]的计算是要用到前一列 也就是j-1这一列。。。所以按照列来求。。
书上给的我觉得有一点问题。。。一个循环里的问题
初始化代码
void RMQ_init()
{
      for(int i=1;i<=n;++i)
            d[i][0]=arr[i];
      for(int j=1;(1<<(j))<=n;++j)
      {
            for(int i=1;i+(1<<(j-1))<=n;++i)
            {
                  d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
            }
      }
}

查询也比较简单 就是令 k 是满足 (1<<k) <(r-l+1) 最大的整数
那么 d[l][k] 和 d[r-1<<(k)+1][k] 就覆盖了l到r这个区间 
中间重叠元素也不影响 因为是取最小值么。。

查询代码如下

int RMQ_query(int l,int r)
{
      while((1<<(k+1))<=(r-l+1))
      k++;
      return min(d[l][k],d[r-(1<<k)+1][k]);
}

这样就可以了。。
再来说这道题吧。。
输入n 和 q 然后是n个数   然后 q 个询问 每个询问 l r
给出ans是l 和 r之间最大值和最小值的差。
其实就是RMQ问题再预处理一个 区间最大值么。。

敲完 吃了饭 wa了一次 然后AC了。。
wa是因为d数组开小了
我直接输出log2(maxn) 是15 忽略掉了一个问题
d的size应该是16 因为从1开始 而且0也用了。

#include<iostream>
#include<cstdio>
#include<cstring>

#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))

using namespace std;
const int maxn=50005;
int n,q;
int arr[maxn];

int dmin[maxn][16];
int dmax[maxn][16];

void RMQ_init()
{
      int i,j;
      for(i=1;i<=n;++i)
      {
            dmin[i][0]=arr[i];
            dmax[i][0]=arr[i];
      }
      for(j=1;(1<<j)<=n;++j)
      {
            for(i=1;(i+(1<<(j-1)))<=n;++i)
            {
                  dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);
                  dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
            }
      }
}

int RMQ_Query(int l,int r)
{
      int k=0;
      while((1<<(k+1)<=(r-l+1)))k++;
      int Min=min(dmin[l][k],dmin[r-(1<<(k))+1][k]);
      int Max=max(dmax[l][k],dmax[r-(1<<(k))+1][k]);
      return Max-Min;
}



int main()
{

      int i,l,r,Min,Max;
      while(scanf("%d %d",&n,&q)!=EOF)
      {
            for(i=1;i<=n;++i)
            scanf("%d",&arr[i]);
            RMQ_init();
            while(q--)
            {
                  scanf("%d %d",&l,&r);
                  printf("%d\n",RMQ_Query(l,r));
            }
      }
      return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值