POJ 3264.Balanced Lineup-RMQ(ST)详解

 

先写一道水题的博客,为后面要写的博客做一个铺垫。

ヾ(◍°∇°◍)ノ゙

 

RMQ(Range Minimum/Maximum Query),即区间最值查询,对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。

时间复杂度:

1、朴素(即搜索),O(n)-O(qn) online。
2、线段树,O(n)-O(qlogn) online。
3、ST(实质是动态规划),O(nlogn)-O(q) online。
ST算法(Sparse Table),以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是max(d[a,k], d[b-2^k+1,k]),
其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。d的求法可以用动态规划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。

传送门:一篇写的容易理解的博客             2/21/2018 5:12:00 PM

 -------------------------------------------------------分割线-----------------------------------------------------------      

                                                      2/22/2018 10:27:00 PM

本来不想写详细一些,但是我发现其他的博客写的我越看越乱,实在是受不了了。

当然我自己写的也乱七八糟,但是我自己能看懂啊。

这里说dp思想的(ST),RMQ就是利用二进制倍增的思想,对数据进行最值比较。关于倍增的思想,传送门:小白兔

该算法通过将数据进行类似二分的操作,将数据进行比较。说的乱七八糟,不如直接解释样例来的实际。

先贴代码:

 1 void ST(){
 2     for(int i=1;i<=n;i++)
 3         mm[i][0]=mi[i][0]=a[i];
 4     for(int j=1;(1<<j)<=n;j++){
 5         for(int i=1;i+(1<<j)-1<=n;i++){
 6             mm[i][j]=max(mm[i][j-1],mm[i+(1<<(j-1))][j-1]);
 7             mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
 8         }
 9     }
10 }

 

还是用其他题解的样例,我用这个推的。

样例3 2 4 5 6 8 1 2 9 7

首先应该知道1<<n是什么东西,位运算,就是左移位,想一下什么时候二进制的能向左移一位,当然是进位了,怎么才能进位啊,就是乘个2才能进位啊。

就是这个意思,1<<0是1,我智障,一开始当成2,我说怎么越推越不对。。。(T▽T)

开始咸鱼行动(只推一部分,讲道理,自己手推一遍就能明白,手推一遍还不懂的,就真的是过分了,就比我还智障了,我是最智障的)

一开始的时候就是比较自己,不管最大还是最小肯定都是自己,mm[1][0]=3,mm[2][0]=2,mm[3][0]=4。。。所以直接进行下一步。只推求最大值的(最小值也一样)

mm[1][1]=max(mm[1][0],mm[1+(1<<0)][0])=max([1][0],mm[1+1][0])=max([1][0],mm[2][0])==>max(3,2)=3;

mm[2][1]=max(mm[2][0],mm[2+(1<<0)][0])=max([2][0],mm[2+1][0])=max([2][0],mm[3][0])==>max(2,4)=4;

mm[3][1]=max(mm[3][0],mm[3+(1<<0)][0])=max([3][0],mm[3+1][0])=max([3][0],mm[4][0])==>max(4,5)=5;

mm[1][2]=max(mm[1][1],mm[1+2][1])=max(mm[1][1],mm[3][1])==>max(3,2,4,5)=5;

mm[2][2]=max(mm[2][1],mm[2+2][1])=max(mm[2][1],mm[4][1])==>max(2,4,5,6)=6;

                                                      max(4,5,6,8)=8;

                                                                                                           max(5,6,8,1)=8;

                                                                                                           max(6,8,1,2)=8;

接下来的mm[1][3]之类的也是这种操作,其实就是比较8个数的最值了(并不是真的比较8个数,而是通过前面假装比较4个数的得出来的,都是动态规划直接用上一个状态的数据得出来的)中间重复的一部分就直接省略过去了,这里就会省很多时间,这就是算法的美丽所在啊,所有能节省时间的算法都是无敌的。

这个操作是神操作,就是通过这种操作进行倍增的比较,也是动态规划美丽的地方(。◕ᴗ◕。)(仔细看看橙色的地方就懂了)。

将中间重复的2和4的比较就省略过去了(因为没必要),自己多推几个就明白了(懒是没用的),不能只会用,还要知道为什么这个可以这样用。

就酱,满足~ing

 

接下来是怎么找区间的最值呢?这里就好理解了。就是将要比较的区间分成两部分,通过状态转移就得出来。首先先算一下最多能2的多少次方满足区间长度,然后按这个长度将区间分开。

还是那个例子 3 2 4 5 6 8 1 2 9 7

假设我要找区间1--6的最大值,首先先算一下,1<<2就是2的平方=4的长度不会超过6-1+1=5,1<<3就是8了,不可以。所以就是找从第一个数开始的前4个数的最值和从6开始的倒着数4个数的最值的比较。

(本质上还是dp的思想,状态转移)就是max(3,2,4,5)和max(4,5,6,8)的比较。

 解释的这么详细了,应该可以了。我要喊翠花了。

 

开始上菜。

 

 

Balanced Lineup
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 58952   Accepted: 27598
Case Time Limit: 2000MS

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 ≤ A ≤ B ≤ N), 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

Source

 
 
题目意思就是奶牛,从区间里找最大值和最小值的差。
以前用线段树写过,但是现在感觉还是RMQ比较好(代码短啊,哈哈哈)
 
贴一下RMQ的代码:
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn=5*10000+10;
 8 const int maxm=100;
 9 int a[maxn],mm[maxn][maxm],mi[maxn][maxm];
10 int n,m;
11 void ST(){
12     for(int i=1;i<=n;i++)
13         mm[i][0]=mi[i][0]=a[i];
14     for(int j=1;(1<<j)<=n;j++){
15         for(int i=1;i+(1<<j)-1<=n;i++){
16             mm[i][j]=max(mm[i][j-1],mm[i+(1<<(j-1))][j-1]);
17             mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
18         }
19     }
20 }
21 int RMQ(int l,int r){
22     int k=0;
23     while((1<<(k+1))<=r-l+1)k++;
24     int ans1=max(mm[l][k],mm[r-(1<<k)+1][k]);
25     int ans2=min(mi[l][k],mi[r-(1<<k)+1][k]);
26     return ans1-ans2;
27 }
28 int main(){
29     scanf("%d%d",&n,&m);
30     for(int i=1;i<=n;i++)
31         scanf("%d",&a[i]);
32     ST();
33     while(m--){
34         int l,r;
35         scanf("%d%d",&l,&r);
36         printf("%d\n",RMQ(l,r));
37     }
38     return 0;
39 }

 

 

 

OK啦,溜了,进行下一步。

Go,进击吧,看我咸鱼突刺!!!(〃'▽'〃)

 

 

posted @ 2018-02-21 17:12  ZERO-  阅读(359)  评论(0编辑  收藏  举报