二分比较的熟悉了,这里就指出ta的必要条件:
- 答案具有单调性
- 已知答案的情况下,可以判定答案的可行性
我们的重点是三分
如果说二分针对的是单调函数,那么三分针对的是双调函数
比如求点到椭圆的最近距离的题目,就可以通过三分一次次的逼近
双调函数也就是下面这样的函数
具体步骤和二分大同小异:
- 当f(M1)<f(M2)f(M1)<f(M2)的时候,我们可以断定M1M1一定在最高点(蓝点)的左边
反证法:
假设M1M1在白点的右边,则M2M2也一定在白点的右边
又由f(M1)<f(M2)f(M1)<f(M2)可推出M1>M2M1>M2,与已知矛盾,故假设不成立
此时可以将l=M1l=M1来缩小范围 - 当f(M1)>f(M2)f(M1)>f(M2)的时候,我们可以断定M2M2一定在蓝点的右边
反证法:
假设M2M2在白点的左边,则M1M1也一定在白点的左边
又由f(M1)>f(M2)f(M1)>f(M2)可推出M2<M1M2<M1,与已知矛盾,故假设不成立
此时可以将r=M2r=M2来缩小范围。
在具体实现的时候有两种方法:
int three_divide(int l,int r)
{
while (l<r-1)
{
int mid=(l+r)>>1;
int mmid=(mid+r)>>1;
if (f(mid)>f(mmid))
r=mmid;
else l=mid;
}
return f(l)>f(r)? l:r;
}
double three_divide(double l,double r)
{
double m1,m2;
while (r-l>=eps)
{
m1=l+(r-l)/3;
mr=r-(r-l)/3;
if (f(m1)>f(m2))
r=m2;
else l=m1;
}
return (m1+m2)/2;
}
下面是下凸函数的三分查询:
int three_divide(int l,int r)
{
while (l<r-1)
{
int mid=(l+r)>>1;
int mmid=(mid+r)>>1;
if (f(mid)>f(mmid))
l=mid;
else r=mmid;
}
return f(l)>f(r)? r:l;
}
double three_divide(double l,double r)
{
double m1,m2;
while (r-l>=eps)
{
m1=l+(r-l)/3;
m2=r-(r-l)/3;
if (f(m1)>f(m2))
l=m1;
else r=m2;
}
return (m1+m2)/2;
}