奶牛排序——RMQ
【问题描述】
奶牛在熊大妈的带领下排成了一条直队。
显然,不同的奶牛身高不一定相同……
现在,奶牛们想知道,如果找出一些连续的奶牛,要求最左边的奶牛 A 是最矮的,最右
边的 B 是最高的,且 B 高于 A 奶牛,且中间如果存在奶牛,则身高不能和 A、B 奶牛相同,
问这样的一些奶牛最多会有多少头。
从左到右给出奶牛的身高,请告诉它们符合条件的最多的奶牛数(答案可能是零、二,
但不会是一)。
【输入格式】
第一行一个数 N(2<=N<=100000),表示奶牛的头数。
接下来 N 个数,每行一个数,从上到下表示从左到右奶牛的身高(1<=身高
<=maxlongint)。
【输出格式】
一行,表示最多奶牛数。
【输入样例】Tahort.in
5
1
2
3
4
1
【输出样例】Tahort.out
4
【 样例解析 】
取第 1 头到第 4 头奶牛,满足条件且为最多。
这道题的思路如下:我们可以首先找到整个区间内最高的牛所在的位置和最矮的牛所在的位置。这样的话,答案区间就不可能越过这两头牛了,之后将区间分开递归求解即可。如果高的牛(maxn)在矮的牛(minn)前面,那么递归搜索
(l,maxn),(maxn+1,minn-1),(minn,r) ,如果矮的牛在高的牛前面,那么我们直接用maxn-minn+1更新答案,之后递归搜索(l,minn-1),(maxn+1,r)即可。
这样的话问题就转化成了如何快速求任意一段区间内的最大最小值的位置。这是一个典型的RMQ问题,我们选择使用st表解决。既然只求最大最小值的位置,那么我们就可以不使用st表存值,这样不仅好写而且时间还短。具体的做法就是我们自定义函数来比较编号,每次比较大小结束之后记录当前区间内最大(小)值的位置即可。这样每次递归访问的时候,我们甚至可以做到O(1)。
这道题有很多地方还是非常坑的,比如说你写一个十分正确的RMQ在Lemon或者本机上运行都会导致RE,据说可能是因为递归层数过多而爆栈,这个自己本人调试也调不出来,你只会看到程序在输入所有数据之后报错。可以使用vijos代测,但是我的程序前几次提交全部TLE,结果最后采用了一个优化,就是如果递归的区间长度小于当前的答案就直接返回无需搜索。但还是不行……最后发现是因为开了全局变量的缘故,开成局部变量就过了……蒟蒻至今搞不明白是为什么。
最后上一下代码吧。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<cstdlib> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; const int N = 100005,M = 18; typedef long long ll; int n,h[N],max1[N][M],min1[N][M],ans1; int read() { int ans = 0; char ch,last = ' '; ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') ch = last; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } if(last == '-') ans = -ans; return ans; } int fmax(int c,int d)//自定义比较编号的函数 { if(h[c] > h[d] || (h[c] == h[d] && c < d)) return c; else return d; } int fmin(int c,int d) { if(h[c] < h[d] || (h[c] == h[d] && c > d)) return c; else return d; } void ask(int l,int r) { if(l >= r || ans1 >= r-l+1) return;//这个优化可以省很多时间 int k = log2(r-l+1);//注意这里开cstdlib int g = 1 << k; int maxn = fmax(max1[l][k],max1[r-g+1][k]); int minn = fmin(min1[l][k],min1[r-g+1][k]);//就是这两行坑了无数次!!! if(maxn == minn) return; if(maxn > minn) { ans1 = max(ans1,maxn-minn+1);//更新并递归 ask(l,minn-1); ask(maxn+1,r); return; } else { ask(l,maxn); ask(maxn+1,minn-1); ask(minn,r); return; } } int main() { n = read(); rep(i,1,n) { h[i] = read(); max1[i][0] = i; min1[i][0] = i; } int q = log2(n); rep(j,1,q) { for(int i = 1;i+(1<<j)-1<=n;i++) { max1[i][j] = fmax(max1[i][j-1],max1[i+(1<<(j-1))][j-1]); min1[i][j] = fmin(min1[i][j-1],min1[i+(1<<(j-1))][j-1]); }//st表求最大最小值 } ask(1,n); printf("%d\n",ans1); return 0; }