三分的用途
二分可以用来求解单调函数,三分可以用来求单峰/单谷函数的极值,当我们确定了某个单峰\单谷的函数表达式,就可以在O(log3n)的复杂度求出该函数的极值。
三分的原理
和二分类似,二分是将区间分为两半,每次根据mid的取值判断应该删除哪一个区间。三分则是将区间分成三部分,[l,mid1],[mid1,mid2],[mid2,r],每次根据mid1和mid2的取值来确定舍弃一个区间,从而不断缩小极值的取值范围。
例如求一个二次函数的极值,我们将区间等分为3份,如果f(m1)<f(m2),说明m2更靠近极值点,我们舍弃[l,m1]这个区间,反之舍弃[m2,r],最终就能求出该函数的极值
三分的模板
while (r - l > eps)
{
double mid = (r - l) / 3.0;
double m1 = l + mid;
double m2 = m1 + mid;
if (f(m1) < f(m2))
l = m1;
else
r = m2;
}
例题
2020icpc南京站F-fireworks
题目大意:
制作一个烟花需要n时间,燃放所有烟花需要m时间,做出一个能燃放的烟花的概率是p,问使用最佳策略制作和燃放烟花,能够成功释放烟花的最小时间的期望是多少。
思路:
设每做k个烟花释放为最佳策略,则实验符合几何分布,成功释放烟花的概率为1-(1-p)k,E(k)=(n*k+m)/(1-(1-p)k),求导后发现E(k)有一个零点,是单峰函数,对其三分求极小值即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
long double p;
long double f(long double x)
{
return ((long double)x*n+m)/((long double)1.0-pow((1.0-p),x));
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>m>>p;
int l=0,r=1e9+10;
p*=1e-4;
while(r-l>=1)
{
int m1 = l+(r-l)/3;
int m2 = r-(r-l)/3;
if(f(m1)<f(m2))
r=m2-1;
else l=m1+1;
}
//cout<<l<<endl;
printf("%.15Lf\n",f(l));
}
return 0;
}