POJ 3264--Balanced Lineup(RMQ问题)

30 篇文章 0 订阅
29 篇文章 0 订阅

题意:给定一数组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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值