• RMQ算法 以及UVA 11235 Frequent Values(RMQ)


    RMQ算法

        简单来说,RMQ算法是给定一组数据,求取区间[l,r]内的最大或最小值。

        例如一组任意数据 5 6 8 1 3 11 45 78 59 66 4,求取区间(1,8)  内的最大值。数据量小时,只需遍历一遍就可以,数据量一大时就容易时间超限,RMQ算法是一种高效算法,和线段树差不多(当没有数据的实时更新时),当然两者都需要预处理。

       定义映射f(i,j)=x,即以i为起点,长度为2j 区间内的最大最小值,显而易见f(i,0)为该数本身,那么求f(i,j+1)时;

    可得公式 f(i,j)=max(f(i,j-1),f(i+(1<<j-1),j-1) 即f(1,2)=max(f(1,0),f(2,0))=6;

    代码如下:

    void get_RMQ()
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            maxx[i][0]=x;
            minx[i][0]=x;
        }
        for(int j=1;(1<<j)<=n;j++)
        {
            for(int i=1;i+(1<<j)<=n;j++)
            {
                maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);
                minx[i][j]=min(minx[i][j-1],minx[i+(1<<(j-1))][j-1]);
            }
        }
    }

    例题 UVA 11235 Frequent Values(RMQ)

    You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In addition to that, you are given several queries consisting of indices i and j (1 ≤ i ≤ j ≤ n). For each query, determine the most frequent value among the integers ai , ... , aj.

    Input Specification

    The input consists of several test cases. Each test case starts with a line containing two integers n and q(1 ≤ n, q ≤ 100000). The next line contains n integers a1 , ... , an (-100000 ≤ ai ≤ 100000, for each i ∈ {1, ..., n}) separated by spaces. You can assume that for each i ∈ {1, ..., n-1}: ai ≤ ai+1. The following q lines contain one query each, consisting of two integers i and j (1 ≤ i ≤ j ≤ n), which indicate the boundary indices for the query.

    The last test case is followed by a line containing a single 0.

    Output Specification

    For each query, print one line with one integer: The number of occurrences of the most frequent value within the given range.

    Sample Input

    10 3
    -1 -1 1 1 1 1 3 10 10 10
    2 3
    1 10
    5 10
    0
    

    Sample Output

    1
    4
    3
    题目大意,给定一个非降序数列,求给定区间内出现的某个数出现的最大重复次数。
    RMQ思路:
    因为非降序,所以-1记为第一个数,出现了两次,然后lx[1]记录原数组内-1出现的首位置,rx[1]记录原数组内-1最后出现的位置ans[i]=tot,即ans[1]=ans[2]=1,
    则原数组转化为2,4,1,3;查询l,r时先判断ans[l]是否等于ans[r] 若ans[l]==ans[r] 则说明 区间(l,r)内都是同一个数,所以结果为r-l+1;
    否则 result=max(RMQ(ans[l]+1,ans[r]-1),max(r-lx[ans[r]]+1,rx[ans[l]]-l+1);因为断点处无法准确判断所以单独考虑。
    RMQ代码如下 时间:540ms
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int mod=1e5+4;
    int ans[mod],pos[mod],lx[mod],rx[mod];
    int dp[mod][20],tot,x,n,q,l,r,last;
    int max(int x,int y){
        return x>y?x:y;
    }
    void get_RMQ()//dp预处理
    {
        for(int i=1;i<=tot;i++){
            dp[i][0]=pos[i];
        }
        for(int j=1;(1<<j)<=tot;j++)
        {
            for(int i=1;i+(1<<j)-1<=tot;i++)
            {
                dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int RMQ(int l,int r)//查询
    {
        if(l>r) return 0;
        int k=0;
        while(1<<(1+k)<=r-l+1)k++;
        return max(dp[l][k],dp[r-(1<<k)+1][k]);
    }
    int main()
    {
        while(~scanf("%d",&n)&&n)
        {
            scanf("%d",&q);
            tot=0;
            memset(dp,0,sizeof(dp));
            memset(ans,0,sizeof(ans));
            memset(pos,0,sizeof(pos));
            memset(lx,0,sizeof(lx));
            memset(rx,0,sizeof(rx));
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&x);
                if(i==1){last=x;++tot;lx[tot]=1;}
                if(x==last){ans[i]=tot;pos[tot]++;rx[tot]++;}
                else {ans[i]=++tot;pos[tot]++;lx[tot]=rx[tot]=i;last=x;}
            }
            get_RMQ();
            while(q--)
            {
                scanf("%d%d",&l,&r);
                if(ans[l]==ans[r]) printf("%d
    ",r-l+1);
                else printf("%d
    ",max(RMQ(ans[l]+1,ans[r]-1),max(rx[ans[l]]-l+1,r-lx[ans[r]]+1)));
                //端点处单独考虑
            }
        }
        return 0;
    }

     再来一个线段树代码,时间:730ms

    //线段树查询
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int mod=1e5+4;
    int node[mod*4],ans[mod],pos[mod],lx[mod],rx[mod];
    int last,x,n,q,r,l,tot,cont;
    int max(int x,int y){
        return x>y?x:y;
    }
    void PushUp(int t){
            node[t]=max(node[t<<1],node[(t<<1)+1]);
    }
    void build(int l,int r,int t){//建造线段树
        int mid;
        mid=(l+r)>>1;
        if(l==r){
            node[t]=pos[++cont];//注意数据的导入,并不是与t相等
            return;
        }
        build(l,mid,t<<1);
        build(mid+1,r,(t<<1)+1);
        PushUp(t);
    }
    int query(int ll,int rr,int l,int r,int t){//区间查询
        int k=0;
        int mid=(l+r)>>1;
        if(ll<=l && rr>=r) return node[t];
        if(ll<=mid) k=max(k,query(ll,rr,l,mid,t<<1));
        if(rr>mid) k=max(k,query(ll,rr,mid+1,r,(t<<1)+1));
        return k;
    }
    int main(){
        while(~scanf("%d",&n)&&n){
            scanf("%d",&q);
            tot=0;
            memset(ans,0,sizeof(ans));
            memset(lx,0,sizeof(lx));
            memset(rx,0,sizeof(rx));
            memset(pos,0,sizeof(pos));
            for(int i=1;i<=n;i++){
                scanf("%d",&x);
                if(i==1){last=x;++tot;lx[tot]=1;}
                if(x==last){ans[i]=tot;pos[tot]++;rx[tot]++;}//数据的整合
                else {ans[i]=++tot;pos[tot]++;lx[tot]=rx[tot]=i;last=x;}
            }
            cont=0;
            build(1,tot,1);
            while(q--){
                scanf("%d%d",&l,&r);
                if(ans[l]==ans[r]) printf("%d
    ",r-l+1);
                else printf("%d
    ",max(query(ans[l]+1,ans[r]-1,1,tot,1),max(r-lx[ans[r]]+1,rx[ans[l]]-l+1)));
                //同样,端点处单独考虑
            }
        }
        return 0;
    }
  • 相关阅读:
    正则表达式(含递归用法)
    hive tricks
    树的数据结构
    基本排序算法
    佛祖保佑永无BUG
    客户问:“能再便宜点吗”,90%的销售顾问都回答错了?
    AutoMapper的介绍与使用(二)
    AutoMapper的介绍与使用(一)
    hasattr()、getattr()、setattr()函数的使用
    类与对象-内存存储形态
  • 原文地址:https://www.cnblogs.com/shinianhuanniyijuhaojiubujian/p/7010082.html
Copyright © 2020-2023  润新知