二分
二分分为二分查找和二分答案。
二分查找就是在一个有序的数组中查找一个数是否存在或位置。我们可以从中间找齐,因为数组有序,所以如果该数
我们所需要的数,就可以在区间左边寻找;如果大于,就在右边;如果等于,就最好了,直接处理即可。与之有关的库函数有lower_bound()
和upper_bound()
,作用分别是找有序数组第一个
给定数和第一个
给定数的数。二分查找的时间复杂度取决于数组的长度(记为
),即
。
二分答案就是利用题目的单调性或可二分性,二分出一个候选答案 ,通过一个高效的检查函数检查 是否可行,如果可行,则记录答案。一般可以用来解决“双最”问题,即最大值最小或最小值最大。时间复杂度非常优秀,是 二分次数 单次判定时间复杂度 。以下便是一例。
洛谷P1542
【题意】: 小K
成功地破解了密文。但是乘车到X国
的时候,发现钱包被偷了,于是无奈之下只好作快递员来攒足路费去Orz教主
……
一个快递公司要将
个包裹分别送到
个地方,并分配给邮递员小K
一个事先设定好的路线,小K
需要开车按照路线给的地点顺序相继送达,且不能遗漏一个地点。小K
得到每个地方可以签收的时间段,并且也知道路线中一个地方到下一个地方的距离。若到达某一个地方的时间早于可以签收的时间段,则必须在这个地方停留至可以签收,但不能晚于签收的时间段,可以认为签收的过程是瞬间完成的。
为了节省燃料,小K
希望在全部送达的情况下,车的最大速度越小越好,就找到了你给他设计一种方案,并求出车的最大速度最小是多少。
【输入格式】: 第 行为一个正整数 ,表示需要运送包裹的地点数。
下面 行,第 行有 个正整数 表示按路线顺序给出第 个地点签收包裹的时间段为 ,即最早为距出发时刻 ,最晚为距出发时刻 ,从前一个地点到达第 个地点距离为 ,且保证路线中 递增。
可以认为 为出发的地方到第 个地点的距离,且出发时刻为 。
【输出格式】: 仅包括一个正数,为车的最大速度最小值,结果保留两位小数。
【思路】:
最大值最小,这不就是二分的经典应用吗?所以思路非常简单:先二分出 ,在判定 是否可行。
别开心的太早,这是一道经典的实数二分题,顾名思义,就是在实数上二分。这要注意的地方可多了!比如精度啊什么的。
事实上如果题目要求保留到 位,我们就把答案算到 位即可。比如本题要求保留两位,我们就算到五位,就可以了。
另外,实数二分的常见方法有固定精度法(如本代码)和固定次数法。
【代码】:
const int N=200100;
double l,r,mid,eps=1e-5;
int s[N],t[N],L[N],n;
inline bool check(double mid){
if (mid==0.0) return false;
register long double times=0;
//注意C++中有time关键字,用time会CE,所以用times
for(int i=1;i<=n;i++){
times+=L[i]/mid;
if (times>t[i])
return false;
if (s[i]>times)
times=s[i];
}
return true;
}
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}//这就是快读
int main(){
n=read();
for(int i=1;i<=n;i++){
s[i]=read();
t[i]=read();
L[i]=read();
}
l=0.0;r=1000000.0;//r少一个零都不行!
while (r-l>=eps){
mid=(l+r)/2;
if (check(mid))
r=mid;
else l=mid;
}
printf("%.2lf",l);
return 0;
}