【POJ 3264】Balanced Lineup(RMQ算法||线段树)

博客介绍了如何使用RMQ算法和线段树解决Farmer John组织的 Ultimate Frisbee 游戏中,确定一组连续 cows 的高度差问题。给出输入输出格式,并提供样例。RMQ算法是一种用于快速查找数列中指定区间最小值/最大值的数据结构,适合解答此类区间查询问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Balanced Lineup


Description

For the daily milking, Farmer John’s N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.


Input
Line 1: Two space-separated integers, N and Q.
Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i
Lines N+2..N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.
Output
Lines 1..Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.


Sample Input
6 3
1
7
3
4
2
5
1 5
4 6
2 2
Sample Output
6
3
0

题意:

一句话概括->求出给定区间内最大值与最小值的差值

思路:

此题可用两种方法写。1.线段树;2.新操作->RMQ算法
什么是RMQ?

Range Minimum/Maximum Query,对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。

可以看出这道题就是一道RMQ算法的裸题。
第一次见到RMQ算法,其实它(的预处理)就是个DP的思想,关键在于状态转移方程的理解。上那篇文章讲的很好可以看看。

代码:
RMQ算法版本
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int maxsum[200001][20],minsum[200001][20];
int n,q;

void Init()
{
    int i,j,lg=floor(log10(double(n))/log10(double(2)));
    for(j=1;j<=lg;j++)
        for(i=1;i<=n;i++)
            if(i+(1<<j)-1<=n)
            {
                maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+(1<<(j-1))][j-1]);
                minsum[i][j]=min(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]);
            }
}


int main()
{
    int k,a,b,lg;  
    scanf("%d %d", &n, &q);  
    for (int i = 1; i <= n; ++i){  
        scanf("%d", &k);  
        maxsum[i][0] = minsum[i][0] = k;  
    }  
    Init();  
    while(q--){  
        scanf("%d%d", &a, &b);  
        if (a > b) swap(a, b);  
        lg = floor(log10(double(b-a+1))/log10(double(2)));  
        //max(fmax[a][lg], fmax[b-(1<<lg)+1][lg]),两个区间中间有重叠,因此两者中较大的就是该区间的最大值  
        printf("%d\n", max(maxsum[a][lg], maxsum[b-(1<<lg)+1][lg])-min(minsum[a][lg], minsum[b-(1<<lg)+1][lg]));  
    }  
    return 0;   
} 
线段树版本
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 200005
#define INF 0xffffff
using namespace std;
struct Tree{
    int left;
    int right;
    int min;
    int max;
}tree[MAX<<2];//一般都是4倍
int maxv,minv;

void Build(int l,int r,int i)
{
    tree[i].left=l;
    tree[i].right=r;
    tree[i].max=-INF;
    tree[i].min=INF;
    if(l==r) return ;
    int mid=(l+r)/2;
    {
        Build(l,mid,i*2+1);
        Build(mid+1,r,i*2+2);
    }
}

void Insert(int i,int x,int val)
{
    if(tree[i].left==tree[i].right)
    {
        tree[i].max=tree[i].min=val;
        return ;
    }
        //递归更新最大最小值
    tree[i].max=max(tree[i].max,val);
    tree[i].min=min(tree[i].min,val);

    int mid=(tree[i].left+tree[i].right)/2;
    if(x<mid) Insert(i*2+1,x,val);
    else Insert(i*2+2,x,val);
}

void Query(int l,int r,int i)
{
    if(l<tree[i].left||r>tree[i].right) return ;

    if(l==tree[i].left&&r==tree[i].right)
    {
        maxv=max(tree[i].max,maxv);
        minv=min(tree[i].min,minv);
        return ;
    }

    int mid=(tree[i].left+tree[i].right)/2;
    if(l>mid) Query(l,r,i*2+2);
    else if(r<=mid) Query(l,r,i*2+1);
    else {
        Query(l,mid,i*2+1);
        Query(mid+1,r,i*2+2);
    }

}


int main()
{
    int n,q,val;
    scanf("%d %d",&n,&q);
    Build(1,n,0); //这里用下标从0开始,下面也是(从1开始不知道为何WA了)
    for(int i=0;i<n;i++)
    {
        scanf("%d",&val);
        Insert(0,i,val);
    }
    while(q--)
    {
        int a,b;
        maxv=-INF,minv=INF;//每次都初始化一下maxv,minv
        scanf("%d %d",&a,&b);
        Query(a,b,0);
        printf("%d\n",maxv-minv);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值