二分比较的熟悉了,这里就指出ta的必要条件:
- 答案具有单调性
- 已知答案的情况下,可以判定答案的可行性
我们的重点是三分
如果说二分针对的是单调函数,那么三分针对的是双调函数
比如求点到椭圆的最近距离的题目,就可以通过三分一次次的逼近
双调函数也就是下面这样的函数
具体步骤和二分大同小异:
- 当
f(M1)<f(M2)
f
(
M
1
)
<
f
(
M
2
)
的时候,我们可以断定
M1
M
1
一定在最高点(蓝点)的左边
反证法:
假设 M1 M 1 在白点的右边,则 M2 M 2 也一定在白点的右边
又由 f(M1)<f(M2) f ( M 1 ) < f ( M 2 ) 可推出 M1>M2 M 1 > M 2 ,与已知矛盾,故假设不成立
此时可以将 l=M1 l = M 1 来缩小范围 - 当
f(M1)>f(M2)
f
(
M
1
)
>
f
(
M
2
)
的时候,我们可以断定
M2
M
2
一定在蓝点的右边
反证法:
假设 M2 M 2 在白点的左边,则 M1 M 1 也一定在白点的左边
又由 f(M1)>f(M2) f ( M 1 ) > f ( M 2 ) 可推出 M2<M1 M 2 < M 1 ,与已知矛盾,故假设不成立
此时可以将 r=M2 r = M 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))
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;
}