题目
题意概括:给定一个序列,我们需要找出一个值,使序列每一项到我们找的这个值的距离(就是两个数的绝对值,我们可以想象为坐标轴上的距离)都比它前一项到我们找的这个值的距离大。
分析
看到题目的第一眼我想到了二分,但是答案好像不具备单调性,无法判断到底是往左边继续二分还是往右边继续二分。
虽然说二分不可取反正我没想到二分怎么做,但是这启示了我们一点:我们可以通过缩小边界来确定我们要找的值。
考虑如何缩小边界,我们不妨进行分类讨论。在此之前,先将一些概念定义一下,以方便后续讨论。
r e s res res 为我们需要找的值, a i a_i ai 为当前项, a i − 1 a_{i-1} ai−1 为当前项的前一项, l l l 为我们所确定的左边界, r r r 为我们所确定的右边界。对于 i = 1 i=1 i=1 的这种边界情况我们暂时不考虑,这是写代码时才需要注意的。
-
当 a i > a i − 1 a_i>a_{i-1} ai>ai−1 时,画个数轴我们可以发现, r e s res res 一定会在这两项的中点的左侧,即 r e s res res 一定小于 a 1 + a i − 1 2 \frac{a_1+a_{i-1}}{2} 2a1+ai−1,那么我们的右边界就可以进行缩小了。
-
当 a i < a i − 1 a_i<a_{i-1} ai<ai−1 时,同样的画个数轴,我们可以发现, r e s res res 一定会在这两项的中点的右侧,即 r e s res res 一定大于 a i + a i − 1 2 \frac{a_i+a_{i-1}}{2} 2ai+ai−1,那么我们的左边界就可以进行缩小了。
好了,我们已经知道有解该怎么解决了,那么无解怎么办呢?
无解同样也很简单。
- 如果 l > r l>r l>r 就说明取不到任何值了,可以退出去了。
- 或者 r − l ≤ 1 0 − 6 r-l\le10^{-6} r−l≤10−6 这个时候区间已经很小很小了,而题目已经告诉我们我们要找的 r e s res res 小数点后面只有 3 3 3 位,所以这个时候也是不满足了。
- 当然我们不能漏掉一种很重要的情况,那就是当 a i a_i ai 与 a i − 1 a_{i-1} ai−1 相等时,这个时候无论我们怎么取值,这两项到取值的距离都相等,这也是一种无解情况。
无解和有解我们都知道怎么做了,我们开始写代码。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const double eps = 1e-6; //精度
int n;
int a[N];
double l = -2e9 , r = 2e9; //l和r都是小数,用double存
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n;
for (int i=1 ; i<=n ; i++)
{
cin >> a[i];
}
for (int i=2 ; i<=n ; i++)
{
if (l>=r || r-l<=eps || a[i]==a[i-1]) //3种无解情况的判断
{
cout << "pigeon" << endl;
return 0;
}
if (a[i-1]>a[i]) //有解时缩小左边界
{
double mid = (double)(a[i-1]+a[i])/2;
l = max(mid , l);
}
if (a[i-1]<a[i]) //有解时缩小右边界
{
double mid = (double)(a[i-1]+a[i])/2;
r = min(mid , r);
}
}
if (l>=r || r-l<eps) //由于我们是在循环开始的时候判断的,
//所以我们可能会漏掉最后一项和倒数第二项让边界不满足的情况
{
cout << "pigeon" << endl;
return 0;
}
cout << "lovely\n"; //别忘记输出这个了
cout << fixed << setprecision(7) << l+eps << endl; //题目说不能保留超过10位小数,那我们保留7位
return 0;
}