poj 3264 -Balanced Lineup (RMQ与线段树两种做法)

12 篇文章 0 订阅
3 篇文章 0 订阅
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 ≤ ABN), 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


RMQ模板题,第一次接触,没太深理解。

http://dongxicheng.org/structure/lca-rmq/对LCA与RMQ的相关知识说的非常好,仔细研读。

主要在于RMQ的在线算法中对于区间的划分。

f[i,j]表示从第i个数起,连续2^j个数中的最大数。将i,j平分为两段f[i,j-1],f[i+2^(j-1),j]后,可得dp方程f[i,j]=max(f[i,j-1],f[i+2^(j-1),j])

当你要找寻从第i个数到第j个数时,由于可能无法直接找到该区间,可以改为找到两个区间,区间并集为该区间。

取k=log2(j-i+1),可得i,j之间最大值为max{F[i , k], F[ j - 2 ^ k + 1, k]}

即强行将区间用两个子区间合并代替。

贴个代码,没啥说的:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define Max 200005

int cow[50005],maxs[50005][20],mins[50005][20];

void RMQ(int num){
    for (int i=1; i<=num; i++) {
        maxs[i][0]=cow[i];
        mins[i][0]=cow[i];
    }
    for (int j=1; j<20; ++j) {
        for (int i=1; i<=num; ++i) {
            if (i+(1<<j)-1<=num) {
                maxs[i][j]=max(maxs[i][j-1],maxs[i+(1<<(j-1))][j-1]);
                mins[i][j]=min(mins[i][j-1],mins[i+(1<<(j-1))][j-1]);
            }
        }
    }
}

int main(){
    int n,q,a,b,k,maxx,minn;
    while (cin>>n>>q) {
        for (int i=1; i<=n; i++) {
            scanf("%d",&cow[i]);
        }
        RMQ(n);
        for (int i=0; i<q; i++) {
            scanf("%d%d",&a,&b);
            k=log2(b-a+1);
            int temp=pow(2, k);
            maxx=max(maxs[a][k], maxs[b-temp+1][k]);
            minn=min(mins[a][k], mins[b-temp+1][k]);
            printf("%d\n",maxx-minn);
        }
    }
    return 0;
}

下面添加一种线段树做法:

感觉做法上还是与RMQ有很大不同的,不过细细想下去又有些殊途同归。

无非就是线段树存区间内的最大值最小值,存完以后查询的时候按照构造子树的规则根据查找区间的大小进行调整,感觉用到了二分的思想。

大概就是这样吧了解太浅说不出来什么……最近都是看题解做题感觉对算法理解越来越浅……要改善。

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3


struct Node{
    int l,r;
    int nmin,nmax;
}segtree[600005];

int a[200005],nmax,nmin;

void build(int i,int l,int r){
    segtree[i].l=l;
    segtree[i].r=r;
    if (l==r) {
        segtree[i].nmin=segtree[i].nmax=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(i*2, l, mid);
    build(i*2+1, mid+1,r);
    segtree[i].nmin=min(segtree[i*2].nmin,segtree[i*2+1].nmin);
    segtree[i].nmax=max(segtree[i*2].nmax,segtree[i*2+1].nmax);
}

void query(int i,int l,int r){
    if (segtree[i].nmax<=nmax&&segtree[i].nmin>=nmin) {
        return;
    }
    if (segtree[i].l==l&&segtree[i].r==r) {
        nmax=max(segtree[i].nmax,nmax);
        nmin=min(segtree[i].nmin,nmin);
        return ;
    }
    int mid=(segtree[i].l+segtree[i].r)/2;
    if (r<=mid) query(i*2, l, r);
    else if (l>mid) query(i*2+1, l, r);
    else {
        query(i*2, l, mid);
        query(i*2+1, mid+1, r);
    }
}

int main(){
    int n,l,r,q;
    while (scanf("%d %d",&n,&q)!=EOF) {
        for (int i=1; i<=n; i++) {
            scanf("%d",&a[i]);
        }
        build(1, 1, n);
        for (int i=1; i<=q; i++) {
            scanf("%d%d",&l,&r);
            nmax=-INF,nmin=INF;
            query(1, l, r);
            printf("%d\n",nmax-nmin);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值