三分法求解单峰/谷函数最值的一些应用
三分法求解单峰/谷函数最值的一些应用
回想一下我们在二分的时候二分的那个函数是什么?是一个单调的函数,只会上升或者下降,假如我们要在一个二次函数中求极值,那么二分不再适用(不考虑求导后二分),我们需要三分法来解决这样的问题。
实数域内三分#
这一部分一般都比较好理解,边界问题也较少。
【模版】P3382 三分:不考虑求导后二分。
以这个函数为例:
所谓三分,也就是找出三等分点,然后比较优劣,选取最值即可。
我们发现 离极值更近,所以选取右边作为答案,如此往复。
while(r-l>=eps){
double lmid = l + (r-l)/3.0;
double rmid = r - (r-l)/3.0;
f(lmid)<f(rmid)?l=lmid:r=rmid;
}
这样写,每次会把答案区间减少 。这里给出另一种效率较高的写法:
while(r-l>=eps){
double mid = (r+l)/2.0;
f(mid-eps)<f(mid+eps)?l=mid:r=mid;
}
这样相当于把三分的范围缩得很小,效率接近二分。但是,这种方法有一个缺陷,考虑 过小的时候因为精度有限, 此时可能会导致我们去向错误的分支!而且平时没人卡三分的效率,因为它已经很快了。
当然,你也可以做做 P1883 【模板】三分 | 函数 这题,巩固一下模版。
注意到在这两题中都明确指出了答案是一个单峰或单谷函数,但是实际问题可能没有这么简单。
【例题 1】P2571 [SCOI2010] 传送带:
这题有两个线段,先来想想假设一条线段已知的情况下怎么做。
假设我们已经到达了 上的点 那么考虑下一步接到 上的哪一个点可以最佳,假设两条线段平行,那么显然垂直最短,实际上这题也满足,也就是说明我们可以知道在确定一个线段的时候,可以对另一个来三分。
那么第一维怎么处理呢?实际上这也满足三分的性质,于是我们可以三分套三分。
lld Ax,Ay,Bx,By,Cx,Cy,Dx,Dy,p,q,r,eps=1e-9;
lld Dis(lld x1,lld y1,lld x2,lld y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
lld ff(lld x,lld y){
lld l1=Dis(Ax,Ay,Bx,By)*x,l2=Dis(Cx,Cy,Dx,Dy)*y;
return l1/p+l2/q+Dis(Ax+(Bx-Ax)*x,Ay+(By-Ay)*x,Dx-(Dx-Cx)*y,Dy-(Dy-Cy)*y)/r;
}
lld f(lld x){
lld l=0,r=1;while(r-l>=eps){lld mid=(l+r)/2;
ff(x,mid-eps)>ff(x,mid+eps)?l=mid:r=mid;
}return ff(x,l);
}
void solve(){
ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>Ax>>Ay>>Bx>>By>>Cx>>Cy>>Dx>>Dy>>p>>q>>r;
lld l=0,r=1;while(r-l>=eps){lld mid=(l+r)/2;
f(mid-eps)>f(mid+eps)?l=mid:r=mid;
}cout<<fixed<<setprecision(2)<<f(l)<<'\n';
}
代码非常简短。
正数域内的三分#
很多时候,我们的重心都是解决一些序列问题,而这些序列往往不是小数,当其满足单调性的时候我们可以二分,而满足单峰或单谷的时候我们就可以三分答案,而正数的三分也意味着更多的细节。
CF1355E Restorer Distance:
首先,对于一切三分题,首要的是意识到能三分来做,也就是意识到题目的答案函数是单峰或单谷的函数。
对于这题,我们显然可以意识到假如我把砖垒的很高,那么代价显然很大,假如只剩下一点点,那么拿走砖的代价也很大,答案肯定是中间的某个数,于是考虑三分。
那么三分什么呢?显然不能无脑三分最后的代价值,不是不好计算,而是这个函数根本不满足上述要求。我们考虑 表示当把墙的高度定为 时的花费,这个显然符合我们的推理,最后计算答案即可。
int l = 0,r = 1e9,ans = 1e18;
while(l+1<r){
int mid = (l+r)>>1;
int x = calc(mid-1),y = calc(mid+1);
if(x<y){
ans = min(ans,x);
r = mid;
}else{
ans = min(ans,y);
l = mid;
}
}
区别于实数的三分,我们可以采用如第四行的写法,因为差值几乎是一定会造成答案的差异的,注意我说的是几乎。
最后我们输出的时候不能直接 calc(ans)
,你考虑到我们在第二行写的判断语句是什么意思?就是说数字个数大于等于 个我才三分,是不是意味着退出循环时,我们可能剩下 两个数字?那么我们还需要分别计算这两个的值才能得到最终答案。
【经验】P3745 [六省联考 2017] 期末考试 :
总体思路差不多。我们只需要三分那个最晚的时间即可。
为什么只写这么一点呢?因为笔者太菜,做的题太少,下次遇到好题了可以补充。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效