感觉hihocoder和别的OJ有点不一样呢,因为可以看到关于一些常用算法的解释,感觉这个三分法讲的蛮通俗易懂呢:
********************************************************* 下面是引用hihocoder上的讲解 *********************************************************
在之前的几周中我们了解到二分法作为分治中最常见的方法,适用於单调函数,逼近求解某点的值。
但当函数是凸形函数时,二分法就无法适用,这时就需要用到三分法。
从三分法的名字中我们可以猜到,三分法是对于需要逼近的区间做三等分:
我们发现lm这个点比rm要低,那么我们要找的最小点一定在[left,rm]之间。如果最低点在[rm,right]之间,就会出现在rm左右都有比他低的点,这显然是不可能的。 同理,当rm比lm低时,最低点一定在[lm,right]的区间内。
利用这个性质,我们就可以在缩小区间的同时向目标点逼近,从而得到极值。
********************************************************* 上面是引用hihocoder上的讲解 *********************************************************
对于1142这个题目,通过在画图我们可以看到,距离给定点P(xp, yp)最近的二次曲线上的点Q,可能有5两种情况:
(1)点在碗左侧
(2)点在碗右侧
(3)点在碗里且在对称轴左侧
(4)点在碗里且在对称轴右侧
(5)点在碗里且在对称轴上
假设中心线和抛物线的交点为M(xm, ym),则进一步还可以看到,(1)和(3)的情况极值点所在的区间都是(-∞,xm),而(2)和(4)的情况极值点所在的区间都是(xm,+∞),需要注意的是点P在对称轴上的情况,当P点yp和M点的ym很近时,最近的点即是M,而yp和ym相距较远时,距离P点最近的点不再时M,而是在抛物线左半边和右半边各有一个。
另外还有几个细节需要注意:
(1)当点在曲线上时,要输出“0.000”而不是单个的"0"
(2)题目要求输出的是最近点的距离且距离精确到小数点后第三位,但是在三分的时候我们分的仍然是x座标的区间,需要注意判断结束的条件。
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
class Quadratic{
private:
double a, b, c;
public:
Quadratic(double aa, double bb, double cc) : a(aa), b(bb), c(cc){}
double operator()(double x){
return a*x*x + b*x + c;
}
};
class DistanceXY{
private:
double x0, y0;
Quadratic fun;
public:
DistanceXY(double a, double b, double c, double x, double y) : x0(x), y0(y), fun(a, b, c){}
double operator()(double x){
return sqrt(pow(x0 - x, 2) + pow(fun(x) - y0, 2));
}
};
double minPeak(double l, double r, double esp, DistanceXY fun)
{
double lm, rm, d;
while(r - l >= esp || fabs(fun(r) - fun(l)) >= esp){
d = (r - l) / 3;
lm = l + d;
rm = r - d;
if(fun(lm) < fun(rm)) r = rm;
else l = lm;
}
return fun(l);
}
int main()
{
int a, b, c, x, y;
double p;
scanf("%d%d%d%d%d", &a, &b, &c, &x, &y);
if(a*x*x + b*x + c == y) puts("0.000");
else{
p = -0.5 * b / a;
if(x < p) printf("%.3f\n", minPeak(-300, p, 1e-3, DistanceXY(a, b, c, x, y)));
else if(x > p) printf("%.3f\n", minPeak(p, 300, 1e-3, DistanceXY(a, b, c, x, y)));
else{
double d = fabs(b * b * -0.25 / a + c - y);
printf("%.3f\n", min(d, minPeak(p, 300, 1e-3, DistanceXY(a, b, c, x, y))));
}
}
return 0;
}