POJ 3264--Balanced Lineup(RMQ問題)

題意:給定一數組a[i],輸入任意範圍內的最大最小值之差。

題解:典型的RMQ(range min/max query)問題,對於此種問題,有三種較好的方法。

  • 線段樹:易知對於區間問題,可以使用區間樹較好地解決。在建樹時可以使用遞歸分治的方法,將[a,b]分成兩段(a == b則直接返回對應數組值),在分治函數返回時,只需要比較左右兩個子區間的最大最小值就能求出[a,b]之間的最大最小值。
  • ST算法:動態規劃的方法,以最小值爲例
  1. 設dp[i][j] = min{a[k],i <= k <= i+2^j-1},即dp[i][j]爲從dp[i]開始到第2^j個數爲止的最小值,顯然dp[i][0] = a[i]。
  2. 當j >= 1時,我們可以把上述規模爲2^j的區間分成兩部分,即[i,i+2^(j-1)-1]以及[i+2^(j-1),i+2^j-1],於是得到動態轉移方程:dp[i][j] = min{dp[i][j-1],dp[i+2^(j-1)][j-1]}。
  3. 當查詢區間[a,b]的最小值時我們同樣可以把此區間分成兩部分,假設分成2^n的兩部分(可能有重疊),即有兩區間[a,a+2^n-1]以及[b-2^n+1,b],易知要滿足條件a + 2^n - 1 >= b - 2^n +1,得n >= log2(b-a+2)-1
  • 第三種方法較爲複雜,可參考http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor#Range_Minimum_Query_%28RMQ%29

線段樹:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define maxN 50005
#define maxM 20

int height[maxN];
class node
{
public:
    int minHeight,maxHeight;
};
node tree[maxN*3];

class solve
{
private:
    int N,Q;
    int A,B;
    int minHeight,maxHeight;
public:
    solve(int n,int q):N(n),Q(q)
    {
        processIn();
        buildTree(1,1,N);
        while(Q--)
        {
            scanf("%d%d",&A,&B);
            minHeight = 0X7FFFFFFF;
            maxHeight = 0;
            Search(1,1,N,A,B);
            printf("%d\n",maxHeight-minHeight);
        }
    }
    void processIn();
    void buildTree(int nodeNo,unsigned short a,unsigned short b);
    int Search(int nodeNo,unsigned short A,unsigned short B,unsigned short a,unsigned short b);
};

int solve::Search(int nodeNo,unsigned short A,unsigned short B,unsigned short a,unsigned short b)
{
    if(A == a&&B == b)
    {
        minHeight = min(minHeight,tree[nodeNo].minHeight);
        maxHeight = max(maxHeight,tree[nodeNo].maxHeight);
        return 0;
    }
    int left = nodeNo<<1;
    int right = left|1;
    unsigned short mid = (A+B)>>1;
    if(a > mid)
    {
        Search(right,mid+1,B,a,b);
    }
    else if(b <= mid)
    {
        Search(left,A,mid,a,b);
    }
    else
    {
        Search(left,A,mid,a,mid);
        Search(right,mid+1,B,mid+1,b);
    }
    return 0;
}

void solve::buildTree(int nodeNo,unsigned short a,unsigned short b)
{
    if(a == b)
    {
        tree[nodeNo].minHeight = tree[nodeNo].maxHeight = height[a];
        return ;
    }
    int left = nodeNo<<1;
    int right = left|1;
    unsigned short mid = (a+b)>>1;
    buildTree(left,a,mid);
    buildTree(right,mid+1,b);
    tree[nodeNo].minHeight = min(tree[left].minHeight,tree[right].minHeight);   //取左右子樹中最小值的較小值
    tree[nodeNo].maxHeight = max(tree[left].maxHeight,tree[right].maxHeight);   //取左右子樹中最大值的較大值
    return ;
}

void solve::processIn()
{
    for(int i = 1;i <= N;i++)
    {
        scanf("%d",height+i);
    }
    return ;
}

int main()
{
    int n,q;
    while(~scanf("%d%d",&n,&q))
    {
        solve poj_3264(n,q);
    }
    return 0;
}

ST:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define maxN 50005
#define maxM 20

int height[maxN];
int dp_min[maxN][maxM];
int dp_max[maxN][maxM];

class solve
{
private:
    int N,Q;
    int A,B;
    int max_j;
public:
    solve(int n,int q):N(n),Q(q)
    {
        processIn();
        RMQ_Init();
        while(Q--)
        {
            A = strIn();
            B = strIn();
            printf("%d\n",RMQ(A,B));
        }
    }
    void processIn();
    void RMQ_Init();
    int RMQ(int a,int b);
    int strIn();
};

int solve::strIn()
{
    int num = 0;
    char c;
    while((c = getchar())&&c != '\n'&&c != ' '&&c > 0)
    {
        num *= 10;
        num += c-'0';
    }
    return num;
}

int solve::RMQ(int a,int b)
{
    if(a == b)
        return 0;
    int k = ceil(log((double)(b-a+2))/log(2.0))-1;
    int tmpMin = min(dp_min[a][k],dp_min[b-(1<<k)+1][k]);
    int tmpMax = max(dp_max[a][k],dp_max[b-(1<<k)+1][k]);
    return tmpMax-tmpMin;
}

void solve::RMQ_Init()
{
    int i,j;
    max_j = floor(log((double)(N+1))/log(2.0));
    for(i = 1;i <= N;i++)
    {
        dp_min[i][0] = dp_max[i][0] = height[i];
    }
    for(j = 1;j <= max_j;j++)
    {
        for(i = 1;i+(1<<(j-1)) <= N;i++)
        {
            dp_min[i][j] = min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
            dp_max[i][j] = max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
        }
    }
    return ;
}

void solve::processIn()
{
    getchar();
    for(int i = 1;i <= N;i++)
    {
        height[i] = strIn();
    }
    return ;
}

int main()
{
    int n,q;
    while(~scanf("%d%d",&n,&q))
    {
        solve poj_3264(n,q);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章