实数三分—三分求极值、Line belt
三分求极值
题目是hihocode第1142道题,貌似现在已经无法注册了,我尝试了多次均已失败告终。
废话不多说,直接上题目
Problem Description
在直角坐标系中有一条抛物线y=ax^2+bx+c和一个点P(x,y),求点P到抛物线的最短距离d。
Input
第1行:5个整数a,b,c,x,y。前三个数构成抛物线的参数,后两个数x,y表示P点坐标。-200≤a,b,c,x,y≤200
Output
第1行:1个实数d,保留3位小数(四舍五入)
Sample Input
2 8 2 -2 6
Sample Output
2.437
分析:
看到求最短距离,忽然回想起熟悉的两点间距离公式:d=sqrt((X-x)^2 + (Y-y)^2)
所以只要求得最小d,就求出答案。因为需要输入x,y 所以我们对公式化简,很容易发现,公式化简后类似一个二次函数,所以把求最短距离问题——>求凸函数极值问题,于是乎开始实数三分法,代码如下。
代码实现
# include <cstdio>
# include <iostream>
# include <cmath>
using namespace std;
double a,b,c,X,Y;
double possible( double x )
{
double y = a*x*x+b*x+c;
double res = hypot(X-x,Y-y);
return res;
}
int main(void)
{
cin>>a>>b>>c>>X>>Y;
double l = -233.0;
double r = 233.0;
while ( abs(r-l) > 1e-8 )
{
double m1 = l+(r-l)/3;
double m2 = r-(r-l)/3;
if ( possible(m1) < possible(m2) )
{
r = m2;
}
else
{
l = m1;
}
}
printf("%.3lf\n",(possible(l)+(possible(r)))/2);
return 0;
}
注:代码第9行hypot() 函数是 cmath 头文件的库函数,用于求给定数字的斜边,它接受两个数字并返回斜边的计算结果,即sqrt(xx + yy)。
Line Belt
Problem Description
In a two-dimensional plane there are two line belts, there are two segments AB and CD, lxhgww's speed on AB is P and on CD is Q, he can move with the speed R on other area on the plane.
How long must he take to travel from A to D?
大意:
平面上有两条轨道,AB和CD,轨道上可以坐车,从A到B的速度是P,而从C到D的速度是Q,不在AB或CD上就只有步行了,速度是R。现在问从A到D的最短时间
Input
The first line is the case number T.
For each case, there are three lines.
The first line, four integers, the coordinates of A and B: Ax Ay Bx By.
The second line , four integers, the coordinates of C and D:Cx Cy Dx Dy.
The third line, three integers, P Q R.
0<= Ax,Ay,Bx,By,Cx,Cy,Dx,Dy<=1000
1<=P,Q,R<=10
Output
The minimum time to travel from A to D, round to two decimals.
Sample Input
1
0 0 0 100
100 0 100 100
2 2 1
Sample Output
136.60
Limit
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
分析:
如图所示画的好潦草,所以这道题的的做法是显而易见的就是,在AB处取一点E,CD处取一点F,让TAE+TEF+TFD取得最小,这时候发动我们的单核草履虫的大脑,如果让A直接到D可能距离最短,但是万一速度比较慢,也会使得总时间不是最短,所以需要一段AE和一段FD去分担一部分时间,但是AE与FD的距离又不能太长,长了也会使总时间加大的,所以这时我们分析E点取值,假设F点的取值已经确定,那么TAE+TEF应该随着AE在AB所占比的增大而先减小后增大,所以存在一个最小值点,属于单谷函数同理对于点F,也假设E点已知,TFD+TEF也是单谷函数,单谷函数求极值——三分法,这里有两个三分又互相影响,所以就是一个三分嵌套问题,代码实现如下:
代码实现
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
using namespace std;
#define EPS 1e-6
struct Point {
double x;
double y;
};
Point a, b, c, d, e, f;
double p, q, r;
double dis(Point p1, Point p2) {
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) );
}
double ae_cost(double alpha) {//alpha是某点在某线段内所占的比例
e.x = a.x + (b.x - a.x) * alpha;
e.y = a.y + (b.y - a.y) * alpha;
return dis(a, e) / p+dis(e, f) / r;
}
double all_cost(double alpha) {
f.x = c.x + (d.x - c.x) * alpha;
f.y = c.y + (d.y - c.y) * alpha;
double time =-1 ;
double L = 0.0, R = 1.0;
while (R - L > EPS) {
double mid = (R - L) / 3.0;
double alpha1 = L + mid, alpha2 = R - mid;
if (ae_cost(alpha1) - ae_cost(alpha2)>EPS) {
L = alpha1;
} else {
R = alpha2;
}
}
time = dis(f, d) / q ;
return ae_cost(L) + time;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> a.x >> a.y >> b.x >> b.y;
cin >> c.x >> c.y >> d.x >> d.y;
cin >> p >> q >> r;
double L = 0.0, R = 1.0;
while (R - L > EPS) {
double mid = (R - L) / 3.0;
double alpha1 = L + mid, alpha2 = R - mid;
if (all_cost(alpha1) - all_cost(alpha2)>EPS) {
L = alpha1;
} else {
R = alpha2;
}
}
printf("%.2lf\n", all_cost(L));
}
return 0;
}
吐苦水🤮
这道题代码改了好久,本来写好的运行整数一点问题没有,后来提交hduoj评测怎么也过不去,然后就是自己疯狂试,终于发现如果输入坐标是小数,答案就会出错,
错误代码:
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
using namespace std;
#define EPS 1e-12
struct Point {
double x;
double y;
};
Point a, b, c, d, e, f,ans;
double p, q, r;
double dis(Point p1, Point p2) {
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + 1e-9);
}
double ae_cost(double alpha) {
e.x = a.x + (b.x - a.x) * alpha;
e.y = a.y + (b.y - a.y) * alpha;
return dis(a, e) / p;
}
double all_cost(double alpha) {
f.x = c.x + (d.x - c.x) * alpha;
f.y = c.y + (d.y - c.y) * alpha;
double time = dis(f, d) / q + dis(e, f) / r;
double L = 0.0, R = 1.0;
while (R - L > EPS) {
double mid = (R - L) / 3.0;
double alpha1 = L + mid, alpha2 = R - mid;
if (ae_cost(alpha1) < ae_cost(alpha2)) {
R = alpha2;
} else {
L = alpha1;
}
ans.x=L;
}
return ae_cost(L) + time;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> a.x >> a.y >> b.x >> b.y;
cin >> c.x >> c.y >> d.x >> d.y;
cin >> p >> q >> r;
double L = 0.0, R = 1.0;
while (R - L > EPS) {
double mid = (R - L) / 3.0;
double alpha1 = L + mid, alpha2 = R - mid;
if (all_cost(alpha1) < all_cost(alpha2)) {
R = alpha2;
} else {
L = alpha1;
}
ans.y=L;
}
printf("%.2lf\n", all_cost(L));
cout<<ans.x<<" "<<ans.y;
}
return 0;
}
可以看到,这里分成AE和EF+FD两部分了,这样形成的一个错误就是TAE是单增的随着E的增大,不适用三分法,而且这样求出来的E就是A点了,Tall就是一个根据错误的局部答案推断的综合错误答案,该答案Tall=TAF+TFD,只对这一部分进行三分,ok现在问题迎刃而解,至于为什么输入整数不会错,那是因为输入的不具普遍性正中错误的点😄

浙公网安备 33010602011771号