zoukankan      html  css  js  c++  java
  • RMQ 的入门 hdu1806

    RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干次询问RMQ(i,j),返回数列A中下标在区间[i,j]中的最小/大值。

    这个有很多算法:这里介绍一种比较高效的ST算法解决这个问题。ST(Sparse Table)算法可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

         令dp(i,j)表示从  i  开始的,长度为 2^j  的一段中元素的最小值,即可以递推出dp(i,j)=min(dp(i,j-1),dp(i+2^(j-1)  ,  j-1))。

    代码:

    void ST(int n) {
        for (int i = 1; i <= n; i++)
            dp[i][0] = A[i];
        for (int j = 1; (1 << j) <= n; j++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i++) {
                dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int RMQ(int l, int r) {
        int k = 0;
        while ((1 << (k + 1)) <= r - l + 1) k++;
        return max(dp[l][k], dp[r - (1 << k) + 1][k]);//int k=(int)(log(double(R-L+1))/log(2.0));
    }

    查询是把区间分两块,比较得结果。

    题目:点这里。

    题意:给出一个非降序的整数数组,你的任务是对于一系列询问,回答区间内出现最多的值的次数。

    分析: 非降序,就是说相同的数会连续出现,即就可以对其分块,相同的数为一块,记录块的长度,左右节点。然后就可以分成三部分,L在的块,R在的块,中间的所有块,找出中间的最长块,L,R  在的块的残缺值比较,就可以了,中间的找最长块就用ST算法。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    const int max_=1e5+5;
    int dp[max_][25];//RMQ的数组
    int a[max_];//原数组。
    int coun[max_];//分段数组
    using namespace std;
    void RMQ_init(int t)//初始化+递推
    {
        for(int i=1;i<t;i++)
            dp[i][0]=coun[i];
        for(int j=1;(1<<j)<t;j++)
            for(int i=1;i+(1<<j)-1<t;i++)
        {
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);//int k=(int)(log(double(R-L+1))/log(2.0));
        }
    }
    int RMQ_Q(int l,int r)//RMQ的查询
    {
        if(l>r)
            return 0;//中间这块为0。
        else {
            int k=0;
            while((1<<k+1)<=r-l+1)k++;
            return max(dp[l][k],dp[r-(1<<k)+1][k]);
        }
    }
    
    int main()
    {
        int n;
        while(cin>>n)
        {
            if(n==0)
                break;
            int m;
    
            int right[max_],left[max_],num[max_];//每个段的左右端点,编号。
            int a[max_];
            cin>>m;
            for(int i=1;i<=n;i++)
            {
                cin>>a[i];
            }
           int k=1,l=1,t=1;//,每段长度,段数
            for(int i=1;i<=n;i++)
            {
                 num[i]=t;//编号
                if(a[i]==a[i+1])
                {
                    k++;//长度+1;
                }
                else
                {
                    right[t]=i;//更新右端点
                    left[t]=l;//更新左端点
                    l=i+1;
                    coun[t]=k;//记录段的长度
                    k=1;
                    t=t+1;
                }
            }
           // cout<<t<<endl;
            RMQ_init(t);
            while(m--)
            {
             int l,r;
             cin>>l>>r;
             if(num[l]==num[r])//在同一块时。
             {
                cout<<r-l+1<<endl;
                continue;
             }
             else//分三块。
             {
                 int L=num[l]+1;
                 int R=num[r]-1;
                 int temp=RMQ_Q(L,R);
                int res=max(right[L-1]-l+1,max(temp,r-left[R+1]+1));//三块比较。
                cout<<res<<endl;
             }
            }
        }
    }

    超时,要改成scanf  和printf……
  • 相关阅读:
    深入学习 History 对象管理浏览器会话历史
    js用斜率判断鼠标进入div的四个方向
    致敬各位10年阿里的前端开发
    JavaScript 时间与日期处理实战:你肯定被坑过
    圣杯/双飞翼布局
    ES6入门
    Javascript中类式继承和原型式继承的实现方法和区别
    五十行javascript代码实现简单的双向数据绑定
    JavaScript 巧学巧用
    VUE2.0学习总结
  • 原文地址:https://www.cnblogs.com/linhaitai/p/9703161.html
Copyright © 2011-2022 走看看