还记得二分吗…
何为二分?二分就是取中间值 mid 比较 f(mid) 和目标值的大小, 舍去 L 到 mid 或 mid +1 到 R 中的其中一个区间 不断收敛得到答案.
当然也有实数域上的二分, 只是要注意退出条件是 R - L < eps .
何为三分
那么如果问题转化成抛物线上求最值, 二分的正确性就不存在了, 这时候就需要三分.
三分有一个 Lmid 和 Rmid , 若 Lmid 的答案更劣, 则舍去区间 L 到 Lmid . 以此类推.
如图所示:
如何三分
例: 求开口向下抛物线最高点 ( 凸包
double lmid=l,rmid=r;
while(rmid-lmid>eps){
double temp=(r-l)/3,t1,t2;
lmid=l+temp;rmid=l+temp*2;
t1=calc(lmid);t2=calc(rmid);
if(t1<t2)l=lmid;
else r=rmid;
}
return calc(l);
P2571传送带
题意简述
一个平面, 有线段 AB 和线段 CD , 已知人在线段 AB 上速度为 p , 在线段 CD 上速度为 q , 在其他地方的速度为 r , 求 A 到 D 的最短时间.
分析
本质就是在两条线段上各选一个点, 使时间最短, 明显这是个有两个自变量的二次函数 ( 雾
一层三分很明显不行, 因为另一个点不确定, 没办法计算函数值 f(x) 怎么办?
三分套三分
如下图. 形象点理解就是在一层三分中算函数值时套用另一层三分.
细节
不是每一层三分都必定会进去计算的. 如果当前三分的定义域本来就很窄 (例: 线段两端点重合) , 则需再调用一次函数计算函数值. 否则将返回 0 .
码
#include<stdio.h>
#include<math.h>
const double eps=0.0000001;
double ax,ay,bx,by,cx,cy,dx,dy,p,q,r,lpx,lpy,rpx,rpy,lqx,lqy,rqx,rqy;
double lx,ly,rx,ry,t1,t2;
inline double calc(double X1,double Y1,double X2,double Y2){
double dis1=sqrt((X1-ax)*(X1-ax)+(Y1-ay)*(Y1-ay)),dis2=sqrt((X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2)),dis3=sqrt((X2-dx)*(X2-dx)+(Y2-dy)*(Y2-dy));
return dis1/p+dis2/r+dis3/q;
}
inline double solve(double x,double y){
lqx=cx;lqy=cy;rqx=dx;rqy=dy;
double t1,t2,lx=lqx,rx=rqx,ly=lqy,ry=rqy;
while(fabs(rqx-lqx)>eps||fabs(rqy-lqy)>eps){
double tx=(rqx-lqx)/3,ty=(rqy-lqy)/3;
lx=lqx+tx;ly=lqy+ty;rx=lqx+tx*2;ry=lqy+ty*2;
t1=calc(x,y,lx,ly);t2=calc(x,y,rx,ry);
if(t1>t2)lqx=lx,lqy=ly;
else rqx=rx,rqy=ry;
}
return calc(x,y,lx,ly); //
}
int main(){ scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",&ax,&ay,&bx,&by,&cx,&cy,&dx,&dy,&p,&q,&r);
lpx=ax;lpy=ay;rpx=bx;rpy=by;lqx=cx;lqy=cy;rqx=dx;rqy=dy;
while(fabs(rpx-lpx)>eps||fabs(rpy-lpy)>eps){
double tx=(rpx-lpx)/3,ty=(rpy-lpy)/3;
lx=lpx+tx;ly=lpy+ty;rx=lpx+tx*2;ry=lpy+ty*2;
t1=solve(lx,ly);t2=solve(rx,ry);
if(t1>t2)lpx=lx,lpy=ly;
else rpx=rx,rpy=ry;
}
printf("%.2lf",solve(lx,ly)); //
return 0;
}