P1883 【模板】三分 | 函数
我们知道,二分可以对单调函数求解近似解。它通过一步一步缩小答案的区间范围,最终确定答案。
三分也是一种类似的算法。
对一个定义域为
引用百度百科
单峰函数是在所考虑的区间中只有一个严格局部极大值(峰值)的实值函数。如果函数
f(x) 在区间[a, b] 上只有唯一的最大值点C ,而在最大值点C 的左侧,函数单调增加;在点C 的右侧,函数单调减少,则称这个函数为区间[a, b] 上的单峰函数。
类似地,我们得到单谷函数的定义。
例如,对于二次项系数大于
特殊地,我们将一次函数也视为单峰函数和单谷函数。
三分,则可以对于单谷函数或单峰函数,确定它的极大值和极小值。
~其实不用严格单调也可以~
考虑以下代码
double l=0,r=1000;
while(r-l>eps){
double mid1=(2*l+r)/3;
double mid2=(l+2*r)/3;
if(f(mid1)<f(mid2)){
r=mid2;
}else{
l=mid1;
}
}
对于一个区间
我们取两个点。
即
当
同理,可以得到
所以,我们就可以通过,
对于整数上的二分和三分,我们终止算法的条件写成
关于其时间复杂度,
在三分中,每次可以将目前的范围缩小到
故时间复杂度为
在我们掌握了三分法之后,回到这个题目。结合题目的标题我们很显然能直接猜到
具体地,我们怎么去理性地得到这个结论呢?
考虑数学归纳法
已知
假设
则只要证明
则要证 若
关于这一点,通过图像上可以轻易地去理解,但是证明较为困难,三位同学给出的证明包括,
-
通过讨论所有情况来证明
-
傅里叶变化
-
一种极其抽象的方法,通过在函数上取点反证
总之,证明略。
以下为代码:
#include<bits/stdc++.h>
using namespace std;
const int N=114514,inf=INT_MAX;
const double eps=1e-9;
int n;
int a[N],b[N],c[N];
inline double f(double x){
double maxn=-inf;
for(int i=1;i<=n;i++){
maxn=max(maxn,a[i]*x*x+b[i]*x+c[i]);
}
return maxn;
}
signed main() {
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i];
}
double l=0,r=1000;
while(r-l>eps){
double m1=(2*l+r)/3;
double m2=(l+2*r)/3;
if(f(m1)<f(m2)){
r=m2;
}else{
l=m1;
}
}
cout<<fixed<<setprecision(4)<<f(l)<<endl;
}
return 0;
}