题意:
机器人初始能量为E
,下一个建筑高 H
,如果当前能量大于H
,那么跳到下一个建筑的能量变为 E + (E-H)
,否则能量为E-(H-E)
,其实两种情况可以归结为一种情况:2E - H
,目标是到达第N
个建筑,且 在这个过程中能量值不能为负数,问机器人初始能量E
最少为多少?
思路:
对于 “最少/最大是多少” 这类问题,可以想一想是否能用二分来做
那么如何确定是否能用二分呢?
回顾一下二分的具体步骤:
步骤① 判断 是否具有二段性的性质,或者 单调性(如果一个题目具有单调性那么一定可以用二分,如果不具有单调性有时候也可以用二分,二段性 “包含” 单调性,单调性是二段性的子集)
-
判断这道题是否具有单调性:如果初始能量
E0
能够满足要求(中间过程非负),那么E'≥E0
时,是否也能够满足要求? -
根据题意,显然
E0
成立之时,所有E'≥E0
的值是成立的,这样的话是具有单调性的,所以E0
一定是个最小的值,且可以二分。
-
如上图,如果
E0
是满足条件的,那么其后面阴影部分必然都是满足条件的,有单调性就一定具有二段性,我们可以用二分来做本题。 -
二段的性质:某一个能量
E
是否可以满足条件? -
判断条件check:初始值如果是
E0
,那么中间过程是否都是≥0
的? -
我们运用这个判断条件一定二分出答案。
步骤② 考虑二分的形式(划分的方案是 r=mid
or l=mid
)
- 设检查
E
是否成立的函数叫做check
函数,if(check(mid))
(成立),说明 初始能量E0
如果是mid
时,是满足要求的,那么 如果mid
满足要求,所有≥mid
的阴影部分值都满足要求(根据 单调性)。如下图
-
我们查找的目标值 是 最小满足要求的值(注意目标值),显然 目标值在
mid
及其左部分,即[L, mid]
(mid
可能为答案),所以我们在更新区间的时候应该“r=mid
”,对应下图是红色部分。
-
如果
mid
不满足要求,那么意味着 所有≤mid
的部分都不满足要求,且mid
也不满足要求,所以答案在[mid+1, R]
中,所以我们在更新区间的时候应该“l=mid+1
” -
所以有:
int mid = l+r>>1;//由于是r=mid,所以不需要+1
if(check(mid)) r = mid;
else l = mid+1;
- 对应 二分查找模板 第二类,寻找绿色区间左端点。
步骤③ 考虑如何书写check
函数(判断机器人每一步跳跃是否恒≥0
)
-
我们发现每一项都可以由前面一项递推出来,所以我们只需从前往后递推一遍,判断中间有没有出现负数,如果中间过程没有出现过负数那么返回
true
,否则返回false
。 -
注意在中间递推过程中,由于不断
×2
,可能会出现超过1e5
的情况,此时直接返回true
即可!
时间复杂度:
O(nlogn)
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int a[N];
int n;
bool check(int mid)
{
for(int i=0;i<n;++i)
{
mid = 2*mid - a[i];
if(mid<0) return false;
if(mid>=1e5) return true;//中间过程可能爆掉1e5,因此一旦爆掉直接返回true
}
return true;
}
int main()
{
cin>>n;
for(int i=0;i<n;++i)
{
scanf("%d", &a[i]);
}
int l = 1, r = 1e5;
while(l<r)
{
int mid = l+r>>1;
if(check(mid)) r = mid;
else l = mid+1;
}
cout<<l<<endl;
return 0;
}