目录
介绍:
说起三分,肯定要提到三分的近亲——二分。
回顾一下二分是什么:对于一个区间,求得其目标(可以是最值),我们可以采取折半查找,若一边的条件不满足,则继续向另外一边折半下去,直到l==r(有些时候有误差),此时的mid即为所求。这样的算法时间复杂度是log量级的。
而三分算法的话,时间复杂度也是log量级的,具体的话好像是O()(省略常数)
其做法是:将给定的区间平均分成3段,比较这三段端点的值,然后与二分一样查找,直到找到为止
那么,三分与二分有什么不同呢?
二分法适用于一个单调序列,请注意,一定是单调!!!
三分法则适用于一个凸函数(上凸与下凸,就是二次函数基本形式中的a<0,a>0的情况)
问题引入:
题目描述
如题,给出一个N次函数,保证在范围[l,r]内存在一点x,使得[l,x]上单调增,[x,r]上单调减。试求出x的值。
输入格式
第一行一次包含一个正整数N和两个实数l、r,含义如题目描述所示。
第二行包含N+1个实数,从高到低依次表示该N次函数各项的系数。
输出格式
输出为一行,包含一个实数,即为x的值。四舍五入保留5位小数。
输入输出样例
输入 #1
3 -0.9981 0.5
1 -3 -3 1
输出 #1
-0.41421
说明/提示
时空限制:50ms,128M
数据规模:
对于100%的数据:7<=N<=13
样例说明:
如图所示,红色段即为该函数f(x)=x^3-3x^2-3x+1在区间[-0.9981,0.5]上的图像。
当x=-0.41421时图像位于最高点,故此时函数在[l,x]上单调增,[x,r]上单调减,故x=-0.41421,输出-0.41421。
(Tip.l&r的范围并不是非常大ww不会超过一位数)
补充:秦九韶算法
在本题中设计一个函数的计算:
首先,拿到这个函数,学过因式分解的蒻蒻都会将x每一项都提出来,我们是否可以试试看?
那么f(x)就是如下形式:
……
然后就会发现一个递推关系:
int f(int x)
{
int F=0;
for(int i=n;i>=0;i--)
F=F*x+a[i];
return F;
}
但是这道题就是需要用double类型,更改一下就好了
解法:
暴力
这个从区间l~r开始枚举,请记住,循环变量i的数据类型是double型!!!
套用秦九韶算法求得函数的值,然后发现f(x+1)<f(x)的时候,输出x即可
三分
本道题目我们用一个更加智慧的办法:三分法,将这个函数均匀分成三段,然后不断更新区间,就可以查到最值(的横坐标)
三分
思路:(以上凸函数为例,下凸函数反之)
既然是分成三段,那么根据植树问题,l,r之间必然有两个点(lmid、rmid),并且这两个点之间的间距等于靠左的点到l的间距,等于靠右的点到r的间距。那么,就可以给这两个点赋值:
k=(r-l)/3;
lmid=l+k;rmid=r-k;
接着就是判定条件:
根据函数的单调性,在这个上凸函数上,在极值点左边的必然是单调递增,右边的反之。
所以说,如果f(lmid)>f(rmid)的话,那么说明lmid离极值点比较近,就需要我们把右边的r向上移动至原来rmid的位置
否则,反之。
这里的否则包含了f(lmid)==f(rmid)的情况,那就是极值点在正中间,我觉得可以直接输出(输出的时候要注意比较mid和mid+1的函数值,mid=(lmid+rmid)/2)了。
但是有很多dalao没有这么写,如果继续跑下去的话也没有大问题,我们还是学一下dalao们吧,因为撞到lmid==rmid的概率很小
下面,就直接贴代码吧
代码:
一、函数为上凸函数
int型:
int three(int l,int r)
{
while(r-l>=1)//一个很小的数,请根据题目设定
{
int k=(r-l)/3;
int lmid=l+k,rmid=r-k;
if(f(lmid) > f(rmid))
r=rmid;
else
l=lmid;
}
return f(l)>f(l+r/2) ? l : (l+r)/2;//比较可能有误差
}
double型:
double three(double l,double r)
{
while(r-l>=0.0000001)//一个很小的数,请根据题目设定
{
double k=(r-l)/3.0;
double lmid=l+k,rmid=r-k;
if(f(lmid) > f(rmid))
r=rmid;
else
l=lmid;
}
return f(l)>f(l+r/2) ? l : (l+r)/2;//比较可能有误差
}
二、函数为下凸函数
int型:
int three(int l,int r)
{
while(r-l>=1)//一个很小的数,请根据题目设定
{
int k=(r-l)/3;
int lmid=l+k,rmid=r-k;
if(f(lmid) > f(rmid))
r=rmid;
else
l=lmid;
}
return f(l)>f(l+r/2) ? l : (l+r)/2;//比较可能有误差
}
double型:
double three(double l,double r)
{
while(r-l>=0.0000001)//一个很小的数,请根据题目设定
{
double k=(r-l)/3.0;
double lmid=l+k,rmid=r-k;
if(f(lmid) < f(rmid))
r=rmid;
else
l=lmid;
}
return f(l)>f(l+r/2) ? l : (l+r)/2;//比较可能有误差
}
注:本蒟蒻没有试验过int型的正确性,而且三分的题目大多都是double型的……
若真的出现int的三分,还请读者试验一下,不可尽信,也不可不信……
模板题:
luogu 3382
POJ 3737
POJ3301
希望能帮助到你!欢迎dalao提出问题