Description
地理学家们经常要对一段河流进行测量分析。他们从上游开始向下游方向等距离地选择n(<=30000)个点测量水位深度。得到一组数据d1,d2,…,dn,回到实验室后数据分析员根据需要对数据进行分析,发掘隐藏在数据背后的规律。最近,乌龙博士发现某种水文现象与河床地势有关,于是他指示他手下的分析员要找出一段河流中最大高低起伏差不超过k(k<=100)的最长一段。这看似一个复杂的问题,由于任务紧急,分析员来求助于你,并告诉你博士的所有数据都精确到个位。
Input
输入文件有两行:第一行是整数n,k,分别表示测量点的个数和博士要求的最大水深差(也就是河床地势差)。第二行有n个整数,表示从上游开始依次得到的水位深度 di(1<=i<=n,0<=di<=32767)。Sample Input
6 2
5 3 2 2 4 5Sample Output
4Hint
提示:从第二个测量点到第五个测量点之间的那段:3 2 2 4,他们起伏最大是4-2=2。
看到题目首先想到二分。
二分什么呢?直接二分答案,也就是长度。
二分出的答案m,如何判断是否合法呢?
对于二分出来的答案m,我们枚举每一个长度为m的区间,如果其中有一个区间存在最大减最小<=k,也就是不超过k,说明长度m合法。
但是求区间[l,r]的最值是O(n)的,如何优化?
1.线段树优化,在O(logn)时间求区间最值。
2.RMQ算法优化,以O(nlogn)时间的预处理来O(1)求最值。
这里提供RMQ算法的代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
const int N = 30007;
int mi[N][20], mx[N][20], a[N], lg[N];
int n, k;
inline int min(int x, int y) { return x < y ? x : y; } //自定义函数比STL的更快
inline int max(int x, int y) { return x > y ? x : y; }
void init() //读入
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
mi[i][0] = mx[i][0] = a[i]; //初始化f[i][0]
}
}
void RMQ()
{
lg[1] = 0;
for (int i = 2; i <= n; i++) //线性求对数
lg[i] = lg[i >> 1] + 1;
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++) //这个地方要保证数组不越界
{
mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]);
mx[i][j] = max(mx[i][j - 1], mx[i + (1 << (j - 1))][j - 1]);
} //递推
}
inline int qry_min(int l, int r) //回答区间最小
{
int len = r - l + 1;
return min(mi[l][lg[len]], mi[r - (1 << lg[len]) + 1][lg[len]]); //RMQ过程,重叠区间
}
inline int qry_max(int l, int r) //回答区间最大
{
int len = r - l + 1;
return max(mx[l][lg[len]], mx[r - (1 << lg[len]) + 1][lg[len]]); //RMQ过程,重叠区间
}
int check(int m) //检查答案是否合法
{
for (int i = 1; i <= n - m + 1; i++) //枚举每一个长度为m的区间
if (qry_max(i, i + m - 1) - qry_min(i, i + m - 1) <= k) //存在一个这样的区间
return 1;
return 0;
}
void solve()
{
RMQ(); //先处理好RMQ数组
int l = 1, r = n, ans;
while (l <= r) //二分答案
{
int mid = (l + r) >> 1;
if (check(mid))
l = mid + 1, ans = mid; //记录答案
else
r = mid - 1;
}
printf("%d\n", ans); //输出答案
}
int main()
{
init();
solve();
return 0;
}