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);

        }
    }

}
原文地址:https://www.cnblogs.com/acSzz/p/2625191.html