描述
这一次我们就简单一点了,题目在此:
在直角坐标系中有一条抛物线y=ax^2+bx+c和一个点P(x,y),求点P到抛物线的最短距离d。
提示:三分法
在之前的几周中我们了解到二分法作为分治中最常见的方法,适用于单调函数,逼近求解某点的值。
但当函数是凸形函数时,二分法就无法适用,这时就需要用到三分法。
从三分法的名字中我们可以猜到,三分法是对于需要逼近的区间做三等分:
我们发现lm这个点比rm要低,那么我们要找的最小点一定在[left,rm]之间。如果最低点在[rm,right]之间,就会出现在rm左右都有比他低的点,这显然是不可能的。 同理,当rm比lm低时,最低点一定在[lm,right]的区间内。
利用这个性质,我们就可以在缩小区间的同时向目标点逼近,从而得到极值。
接下来我们回到题目上,抛物线和点之间的距离可以简单的用直线公式计算:
即d = min{sqrt((X - x)2+(aX2+bX+c-y)^2)}
该公式展开后为4次,需要采用求导等方法来求极值。对于计算机编程来说是很麻烦的一件事。
进一步观察题目,我们可以发现根据带入的X值不同,d的长度恰好满足凸形函数。
而我们要求的最短距离d,正好就是这个凸形函数的极值。
那么三分法不就正好可以用来解决这道题目了么?
需要注意在解题过程中一定要想清楚如何划分区间,我们求的各个变量到底是什么含义。
另外,这道题还有一个小小的trick,在解决的时候请一定要小心。
Close
输入
第1行:5个整数a,b,c,x,y。前三个数构成抛物线的参数,后两个数x,y表示P点坐标。-200≤a,b,c,x,y≤200
输出
第1行:1个实数d,保留3位小数(四舍五入)
Sample Input
2 8 2 -2 6
Sample Output
2.437
参考笔记:https://blog.youkuaiyun.com/chenxiaoran666/article/details/79937474
https://blog.youkuaiyun.com/u012469987/article/details/50897291
#include <cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int maxn = 0x3f3f3f3f;
const int N=1e6+10;
double a,b,c,x,y;
double dis(double x1){
return sqrt(pow(x1-x,2.0)+pow(y-(a*x1*x1+b*x1+c),2.0));
}
int main(){
// #ifndef ONLINE_JUDGE
// freopen("in.txt","r",stdin);
// #endif // ONLINE_JUDGE
scanf("%lf%lf%lf%lf%lf",&a,&b,&c,&x,&y);
double lb=-200.0-1,ub=200.0+1;
double minv=maxn;
for(int i=0;i<100;i++){
double lmid=(lb+ub)/2;
double rmid=(lmid+ub)/2;
if(dis(lmid)<dis(rmid))
ub=rmid;
else
lb=lmid;
}
// printf("%d %d\n",lb,ub);
printf("%.3f\n",dis(ub));
}
对于HDU2899这一道题目来说,也算是一道入门三分的题目
Now, here is a fuction:
F(x) = 6 * x7+8*x6+7x3+5*x2-yx (0 <= x <=100)
Can you find the minimum value when x is between 0 and 100.
Input
The first line of the input contains an integer T(1<=T<=100) which means the number of test cases. Then T lines follow, each line has only one real numbers Y.(0 < Y <1e10)
Output
Just the minimum value (accurate up to 4 decimal places),when x is between 0 and 100.
Sample Input
2
100
200
Sample Output
-74.4291
-178.8534
题意:
给一函数,该函数在任意Y>0的情况下x在[0,100]内有极小值,求之。
思路一样,下面是AC的代码
#include <cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int maxn = 0x3f3f3f3f;
const int N=1e6+10;
double y;
double dis(double x){
return 6*pow(x,7.0)+8*pow(x,6.0)+7*pow(x,3.0)+5*pow(x,2.0)-y*x;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
int t;
scanf("%d",&t);
while(t--){
scanf("%lf",&y);
double lb=0,ub=100;
for(int i=0;i<100;i++){
double lmid = lb + (ub-lb)/3,rmid = ub - (ub-lb)/3;
if(dis(lmid)<dis(rmid))
ub=rmid;
else
lb=lmid;
}
printf("%.4f\n",dis(lb));
}
}
当然对于HDU2899这一道题目不知道图像的话可以运用模拟退火来处理
参考blog:https://blog.youkuaiyun.com/niiick/article/details/82708665
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
double INF=1000000000;
int n;
double y;
double F(double x){
return 6*pow(x,7.0)+8*pow(x,6.0)+7*pow(x,3.0)+5*pow(x,2.0)-y*x;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
int t;
scanf("%d",&t);
while(t--){
scanf("%lf",&y);
double x=100.0;
double dx[2]={-1.0,1.0};
double t=100.0;
while(t>1e-8){
double nx=-1.0;
while(nx<0||nx>100) nx=x+dx[rand()%2]*t;
double res=F(x)-F(nx);
if(res>=0) x=nx;
else if(exp(res/t)>(double)rand()/(double)RAND_MAX) x=nx;
t*=0.999;//这里如果是0.99,那么精度就不够,会WA,开成0.9999会T
}
printf("%.4f\n",F(x));
}
}
上面套用的算是标准的模拟退火的板子吧