zoukankan      html  css  js  c++  java
  • RMQ 详解及 题目

    RMQ(Range Minimum/Maximum Query)问题:
     

      RMQ问题是求给定区间中的最值问题。当然,最简单的算法是O(n)的,但是对于查询次数很多(设置多大100万次),O(n)的算法效率不够。可以用线 段树将算法优化到O(logn)(在线段树中保存线段的最值)。不过,Sparse_Table算法才是最好的:它可以在O(nlogn)的预处理以后实 现O(1)的查询效率。下面把Sparse Table算法分成预处理和查询两部分来说明(以求最小值为例)。

    预处理:
    预处理使用DP的思想,f(i, j)表示[i, i+2^j - 1]区间中的最小值,我们可以开辟一个数组专门来保存f(i, j)的值。
    例如,f(0, 0)表示[0,0]之间的最小值,就是num[0], f(0, 2)表示[0, 3]之间的最小值, f(2, 4)表示[2, 17]之间的最小值
    注意, 因为f(i, j)可以由f(i, j - 1)和f(i+2^(j-1), j-1)导出, 而递推的初值(所有的f(i, 0) = i)都是已知的
    所以我们可以采用自底向上的算法递推地给出所有符合条件的f(i, j)的值。

    1 for(int i=1;i<=n;i++) 
    2    f[i,0]:=a[i];
    3 
    4 for(int j=1;j<=log(n)/log(2);j++)
    5 {
    6    for(int i=1;i<=n+1-1<<j;j++) 
    7      f[i,j]=max(f[i,j-1],f[i+1<<(j-1),j-1]);  
    8 };

    查询:
    假设要查询从m到n这一段的最小值, 那么我们先求出一个最大的k, 使得k满足2^k <= (n - m + 1).
    于是我们就可以把[m, n]分成两个(部分重叠的)长度为2^k的区间: [m, m+2^k-1], [n-2^k+1, n];


    而我们之前已经求出了f(m, k)为[m, m+2^k-1]的最小值, f(n-2^k+1, k)为[n-2^k+1, n]的最小值

    1 long query(long l,long r)
    2 {
    3   long x=log(r-l+1)/log(2);
    4   return (max(f[l,x],f[r+1-1<<x,x]));
    5 }


    我们只要返回其中更小的那个, 就是我们想要的答案, 这个算法的时间复杂度是O(1)的.
    例如, rmq(0, 11) = min(f(0, 3), f(4, 3))

    感想:对于一个数组里的固定长度的区间,可以只开一个n*1维数组,算f(i,j)的时候可以将f(i,j-1)覆盖,因为用到的只是上一列的情况。

    题目:

    poj 3368 Frequent values

    http://poj.org/problem?id=3368

     

    题意 : 给定 一个 递增的数组 a   给出 q 次询问 求出 l  到 r  最多的     数字相同的个数

     

    如 :10 3 -1 -1 1 1 1 1 3 10 10 10

    输入 1 10

    输出  4

      1 到10 出现最多的是 1 出现了 4 次

     

    题解: 将 连续相同的点 缩成一个节点 ,这个节点包括三个部分  初始位置 ,结束位置 ,个数
      那么询问时 包括三种情况
      1:l 和 r同属于  一个节点   那么 ans = r - l + 1
       
      2: 属于相邻的连个节点  ans =  max ( p[hash[l]].t - l +1,r - p[hash[r]].s + 1);
      3: 属于  l 和 r之间有好几个节点   hash[l]+1  到 hash[r] - 1 的最大值 和 情况 2 进

    View Code
     1 #include<cstdio>
     2 #include<cmath>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<set>
     7 #include<map>
     8 #include<queue>
     9 #include<vector>
    10 #include<string>
    11 #define Min(a,b) a<b?a:b
    12 #define Max(a,b) a>b?a:b
    13 #define CL(a,num) memset(a,num,sizeof(num));
    14 #define maxn  100010
    15 #define inf 9999999
    16 
    17 #define  mod   9973
    18 #define ll  long long
    19 using namespace std;
    20 int hash[maxn],a[maxn];
    21 int dp[maxn][30],id;
    22 
    23 struct node
    24 {
    25     int s;
    26     int t;
    27     int w;
    28 
    29 }p[maxn];
    30 void rmq_init()
    31 {
    32     int i , j;
    33 
    34     for( i = 0; i <= id;++i)dp[i][0] = p[i].w;
    35     int k = log(double(id+1.0))/log(2.0);//  
    36 
    37     for( i = 1;i <= k; ++i)
    38     {
    39         int s = (1 << i-1) - 1;
    40         for(j = 1;j + (1 << i)-1 <= id ;++j)
    41         {
    42             dp[j][i] = max(dp[j][i - 1],dp[j + s ][i - 1]);
    43         }
    44     }
    45 }
    46 int get(int l,int r)
    47 {
    48     int k = log(double(r - l +1.0))/log(2.0);//求的是 求出最大的k,满足2^k<=R-L+1
    49     int s = 1 << k;
    50     return max(dp[l][k],dp[r - s + 1][k]);
    51 }
    52 int main()
    53 {
    54     int n,m,i,l,r;
    55     while(scanf("%d",&n),n)
    56     {
    57         scanf("%d",&m);
    58         for( i = 1;i <=n; ++i)scanf("%d",&a[i]);
    59 
    60         memset(p,0,sizeof(p));
    61          id = 1;
    62         p[1].s = 1;
    63 
    64         for( i = 1; i <= n ;++i)
    65         {
    66             hash[i] = id ;
    67             p[id].w++;
    68             if( a[i] != a[i+1] || i == n)
    69             {
    70                 p[id].t = i;
    71                 if(i == n)break;
    72                 id++;
    73                 p[id].s = i + 1 ;
    74 
    75             }
    76 
    77         }
    78          rmq_init();
    79 
    80          while( m--)
    81          {
    82              scanf("%d%d",&l,&r);
    83              if(hash[l] == hash[r])printf("%d\n",r - l + 1);
    84              else
    85              {
    86                 int num1 = p[hash[l]].t - l +1;
    87                 int num2 = r - p[hash[r]].s + 1;
    88                 int num3 = 0;
    89                 if(hash[r] - hash[l] > 1)
    90                 num3 = get(hash[l]+1,hash[r]-1);
    91                 int ans = max(max(num1,num2),num3);
    92                 printf("%d\n",ans);
    93              }
    94          }
    95     }
    96 }

    poj  3264    Balanced Lineup

    http://poj.org/problem?id=3264

    题意 : 给定 一段数   q此次询问   求出  l 到 r 的  最大值 和最小值的

    View Code
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<queue>
    #include<vector>
    #include<string>
    #define Min(a,b) a<b?a:b
    #define Max(a,b) a>b?a:b
    #define CL(a,num) memset(a,num,sizeof(num));
    #define maxn  60000
    #define inf 9999999
    
    #define  mod   9973
    #define ll  long long
    using namespace std;
    int dp1[maxn][30],dp2[maxn][30],a[maxn];
    int n,m;
    void RMQ_init()
    {
        int temp = 0,i,j,s;
        while( 1<< temp < n)temp++;
    
        for(i = 0;i <= n; ++i)dp1[i][0] = dp2[i][0] = a[i];
    
        for(i = 1; i <= temp ; ++i)
        {
             s = 1 << (i-1);
            for(j = 0; j + s <= n;++j)
            {
                 dp1[j][i] = max(dp1[j][i-1],dp1[j + s][i-1]);
                 dp2[j][i] = min (dp2[j][i-1],dp2[j + s][i - 1]);
            }
        }
    }
    int  get (int l,int r)
    {
        int k = r - l + 1;
        int tmp = 0;
    
    
        while(1 << tmp < k ) tmp++;
        tmp--;
        int s = 1 << tmp;
        int mx = max(dp1[l][tmp],dp1[r + 1 - s][tmp]);
        int mi = min(dp2[l][tmp],dp2[r + 1 -s ][tmp]);
    
        return mx - mi ;
    
    }
    int main()
    {
        int i, l,r;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(i = 1;i <= n; ++i)
            scanf("%d",&a[i]);
    
            RMQ_init();
    
            while(m--)
            {
                scanf("%d%d",&l,&r);
                int ans = get(l ,r);
               printf("%d\n",ans);
    
            }
        }
    
    }
  • 相关阅读:
    新浪微博热门话题(30 分)(字符串)
    新浪微博热门话题(30 分)(字符串)
    任务调度的合理性 (25)拓扑排序
    任务调度的合理性 (25)拓扑排序
    PTA 银行排队问题之单队列多窗口服务(25 分)
    PTA 银行排队问题之单队列多窗口服务(25 分)
    PTA 畅通工程之最低成本建设问题(30 分)(最小生成树 krusal)
    PTA 畅通工程之最低成本建设问题(30 分)(最小生成树 krusal)
    PTA 最大子列和问题(10 分)
    PTA 是否同一棵二叉搜索树(25 分)
  • 原文地址:https://www.cnblogs.com/acSzz/p/2625191.html
Copyright © 2011-2022 走看看