三分法是二分法的变种,他最基本的用途是求单峰函数的极值点。
- 三分适用的情况:有唯一的最大值,满足最大值左侧严格单调递增,右侧严格单调递减(或左减右增)。强调严格单调,这样在确定最值是才能判断最值的位置,否则三分法不能缩小左右边界。
三分整数模板
整数的三分可能具有不确定性,可以通过改变while循环的条件while(l+3<r)来缩小范围,再通过小范围暴力更新答案
- 对于边界的暴力不仅省去了处理边界,甚至常数也有提升,原因未知
凹函数的极小值
int sfmin(){
int l=0,r=1e9;
while(l+2<r){
//cerr<<l<<" "<<r<<endl;
int m1=(r-l)/3+l;
int m2=r-(r-l)/3;
if(cal(m1)>cal(m2))l=m1;
else r=m2;
}
int ans=cal(l);
for(int i=l+1;i<=r;i++)ans=min(ans,cal(i));
return ans;
}
凸函数的极大值
double sfmax(){
int l=1,r=cnt-1;
while(l+2<r){
//cerr<<l<<" "<<r<<endl;
int m1=(r-l)/3+l;
int m2=r-(r-l)/3;
if(cal(m1)<cal(m2))l=m1;
else r=m2;
}
double ans=cal(l);
for(int i=l+1;i<=r;i++)ans=max(ans,cal(i));
return ans;
}
三分小数模板
凸函数的极大值
double l, r;
for(int i = 0; i < 300; i++) {//通过直接确定三分次数直接保证不会出现精度问题
double lmid = l + (r - l) / 3;
double rmid = r - (r - l) / 3;
if(calc(lmid) <= calc(rmid)) l = lmid;
else r = rmid;
}
printf("%.6f\n", calc(l));
凹函数的极小值
double l, r;
for(int i = 0; i < 300; i++) {
double lmid = l + (r - l) / 3;
double rmid = r - (r - l) / 3;
if(calc(rmid) >= calc(lmid)) r = rmid;
else l = lmid;
}
printf("%.6f\n", calc(l));
三分例题
https://codeforces.com/contest/1355/problem/E
https://codeforces.com/contest/939/problem/E
https://www.luogu.com.cn/problem/solution/CF939E