POJ3264 Balanced Lineup

http://poj.org/problem?id=3264

经典的RMQ题目。RMQ问题是求给定区间中的最值问题。朴素算法是O(n)的,用线段树可以将算法优化到O(logn)(在线段树中保存线段的最值)。

不过,只查询的话RMQ算法最合适:它可以在O(nlogn)的预处理以后实现O(1)的查询效率。线段树主要的区别是可以修改区间,RMQ不行。
下面把Sparse Table算法分成预处理和查询两部分来说明。 使用了倍增思想。
预处理:
以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是max(d[a,k], d[b-2^k+1,k]),其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用动态规划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
所以我们可以采用自底向上的算法递推地给出所有符合条件的d(i, j)的值。

查询:
查询的巧妙之处是将[m, n]这段分成两个部分重叠的的区间
于是我们就可以把[m, n]分成两个(部分重叠的)长度为2^k的区间: [m, m+2^k-1], [n-2^k+1, n];

此题几个注意的地方:1. 要注意递推的时候按长度从2^0到2^j递推,j的循环要放在外面;2. log函数用的时候,里面参数是double;3. 求2的power是常用操作,使用左移<<操作非常合适;4. cin/cout会超时,要使用scanf/printf。

#include <iostream>
#include <cmath>

using namespace std;
#define LEN 50005
#define LOG_LEN 20
#define MAX(a,b) (a>b?a:b)
#define MIN(a,b) (a<b?a:b)
#define ABS(a) (a<0?-a:a)
const int INF (1<<30);

int cow[LEN];
int fmin[LEN][LOG_LEN];
int fmax[LEN][LOG_LEN];
int Q;
int N;

void init()
{
    memset(cow, 0, sizeof(cow));
    memset(fmin, 0, sizeof(fmin));
    memset(fmax, 0, sizeof(fmax));

    scanf("%d%d", &N, &Q);
    for (int i = 1; i <= N; i++)
    {
		scanf("%d", &cow[i]); 
    }
}

void build()
{
    // build the RMQ
    for (int i = 1; i <= N; i++)
    {
        fmin[i][0] = cow[i];
        fmax[i][0] = cow[i];
    }
    int size = int (log((double)(N + 1)) / log(2.0));
    
    for (int j = 1; j <= size; j++)
    {
        for (int i = 1; i + (1 << j) - 1 <= N; i++)
        {
            fmin[i][j] = MIN(fmin[i][j - 1], fmin[i + (1 << (j - 1))][j-1]);
            fmax[i][j] = MAX(fmax[i][j - 1], fmax[i + (1 << (j - 1))][j-1]);
        }
    }
}

int query(int left, int right)
{
    int diff = right - left + 1;
    int k = (int) (log((double)diff) / log(2.0));
    int min = MIN(fmin[left][k], fmin[right - (1 << k) + 1][k]);
    int max = MAX(fmax[left][k], fmax[right - (1 << k) + 1][k]);
    return max - min;
}

int main()
{
    init(); 
    build();
    
    // query
    for (int i = 0; i < Q; i++)
    {
        int x, y;
		scanf("%d%d", &x, &y);     
        printf("%d\n", query(x, y));  
    }
    return 0;
}

  

 

posted @ 2013-10-13 14:10  阿牧遥  阅读(185)  评论(0编辑  收藏  举报