感覺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;
}