poj 3264 Balanced Lineup ( ST算法(dp))

题意:

          给出N个数,Q个询问。

           每个询问求区间[L,R]中最大值与最小值之差。

做法:

          ST算法,本质上是一种dp。

          假设用二位数组来保存最大值的信息,其中max[i][j]表示从第i个数开始(每行0号元素不用,即i! = 0),长度为2^j的区间,即[i, i + 2^j-1]。

          则max[i + 2 ^ j][j]表示从第(i + 2 ^ j)个数开始,长度为2 ^j的区间,即[i + 2 ^ j,(i +2 ^ j)+ 2 ^ j-1]。

  很容易发现, [i, i + 2^j-1]和[i + 2 ^ j,i + 2 ^ j+2^j-1]这两个区间刚好组成了区间[i ,i + 2 ^ j+2^j-1]。即[i,i+2 ^ (j+1)-1]。

  而[i,i+2 ^ (j+1)-1]表示的就是从第i个数开始,长度为2^(j+1)的区间。

  于是有max[i][j+1]=MAX(max[i][j],max[i+2^j][j]

  根据这种倍增的思想,可求出所有长度为2^j(0<=j<log2(n))的区间上的最大值。

  对应每个询问[L,R],设len=R-L,x=log2(len) 

  则区间[L,R]可分成[L,L+2^x-1]和[R-2^x+1,R],两个区间中间可能会重叠,但绝不会有间隔。

  于是[L,R]上的最大值即为MAX(max[L][x],max[R-2^x+1][x])

   

  最小值的做法同法,然后相减即为答案。

 

 

#include 
#include 
#include 
using namespace std;
const int MAXN = 50005, MAXM = 22;
int maxh[MAXN][MAXM], minh[MAXN][MAXM];

int main() {
    int n,q,l,r,x;
    while(~scanf("%d%d",&n,&q)) {

        for(int i = 0; i < n; i++) {
            scanf("%d",&x);
            maxh[i][0] = minh[i][0] = x;
        }

        for(int i = 1; (1 << i ) < n; i++)
            for(int j = 0; j + (1 << i) - 1 < n; j++) {
                maxh[j][i] = max(maxh[j][i - 1],maxh[j + (1 << (i - 1))][i - 1]);
                minh[j][i] = min(minh[j][i - 1],minh[j + (1 << (i - 1))][i - 1]);
            }

        while(q--) {
            scanf("%d%d",&l,&r);
            l--;
            r--;
            x = (int) log2(r - l + 1.0) ;
            printf("%d\n",max(maxh[l][x],maxh[r - (1 << x) + 1][x])-min(minh[l][x],minh[r - (1 << x) + 1][x]));
        }

    }
    return 0;
}

 

 

 

 

 

你可能感兴趣的:(poj,ST,ACM)