POJ

来源:互联网 发布:37传奇霸业魂珠数据 编辑:程序博客网 时间:2024/05/29 17:56

题目大意:

给你一串数,50,000个,询问200,000个区间,每次询问输出该区间最大值与最小值的差

分析:

这个因为不用修改,只需要查询,其实用树状数组也是可以的我觉得,但是因为还是有一个log50000,说不定真的会超时,所以还是选择用他给的这个O(1)复杂度的算法吧。

关于st算法:

就是首先预处理:

dp [ i ] [ j ] 表示 从第 i 个数开始 向后数 2j1 个数之后这之间所有的数的最大值(最小值),既, dp [ i ] [ j ] 表示区间 [ i,i+2j1 ] 中 a [ ] 的最大值(最小值)。那么,通过状态转移方程:dp [ i ] [ j ] = max { dp [ i ] [ j-1] , dp [ i+2j1 ] [ j-1 ] } 即可确定所有的 i 以及在可选区间内所有的连续的 j 所对应的 dp [ i ] [ j ] 。时间复杂度O(nlogn)。

关于查询:

查询区间 [ a , b ] 就是 max { dp [ a ] [ k ] , dp [ b-2k+1 ] [ b ] } ; 其中 k=log(b-a+1) 。

代码:

#include<iostream>#include<stdio.h>#include<math.h>#include<string.h>using namespace std;#define maxn 50500int n,q;int dmax[maxn][30];int dmin[maxn][30];int a[maxn];void build(){    for(int i=1;i<=n;i++)dmax[i][0]=a[i];      for(int j=1;(1<<j)<=n;j++)    {        for(int i=1;i+(1<<j)-1<=n;i++)        {            dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);        }    }     for(int i=1;i<=n;i++)dmin[i][0]=a[i];      for(int j=1;(1<<j)<=n;j++)    {        for(int i=1;i+(1<<j)-1<=n;i++)        {            dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);          }    }}int getmax(int l,int r){    int k=0;    while((1<<(k+1))<=r-l+1)k++;    return max(dmax[l][k],dmax[r-(1<<k)+1][k]);}int getmin(int l,int r){      int k=0;      while((1<<(k+1))<=r-l+1)k++;      return min(dmin[l][k],dmin[r-(1<<k)+1][k]);  }int main(){    while(cin>>n>>q)    {        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);        }        build();        int a,b;        for(int i=1;i<=q;i++)        {            scanf("%d%d",&a,&b);            printf("%d\n",getmax(a,b)-getmin(a,b));        }    }}

ST算法的xcx理解:

首先用一种很暴力的方法,如果我求出了任意一个区间的最大值,然后存起来,随查随用,那么查询就是完美的O(1)时间复杂度。但是这样预处理就太慢了,用动态规划应该也需要O(n2)的时间复杂度。然后我就想,能不能对于每个区间起点,我只存有限的几个特定的区间终点的最大值。用二分的思想,我就存logn个。那么现在我就得到了以任意一个点为起点的若干(logn)个特定的区间的最大值。那么现在对于任意一个我想要查询的区间 [ a , b ] ,我现在肯定能知道以 a 为起点的区间终点不超过b的最长的存好的区间的最大值。然后这个区间终点的位置肯定在 ab 两点终点靠近 b 点的位置,因为否则的话,一定存在更大的存好的区间,其区间终点不超过b。然后找以b终点的区间,存好的区间里面,肯定有一个区间起点位置在 ab 两点终点靠 a 一侧的区间。这样我就把求区间 [ a , b ] 的最大值,转换成了求两个已经存好的有交叉部分的子区间的最大值的。

后注:

这波魔板代码抄的真是爽,写了一下午比赛,ac4题,实在懒得想这个代码的细节了,回来要自己再好好写一道rmq的题。

0 0
原创粉丝点击