鏈接:https://cn.vjudge.net/problem/HDU-5017
題意:給出一個橢球面,求橢球面上原點最近的距離。
思路:首先,我們只需要考慮橢球的1/8(因爲它關於原點對稱。),然後,這1/8面上的點到原點的距離肯定是由遠到近再到遠,滿足三分的性質,直接三分即可。也可模擬退火。
1.三分
#include<bits\stdc++.h>
using namespace std;
#define ll long long
const double eps=1e-9;
double a,b,c,d,e,f;
double checkz(double x,double y)
{
double A=c,B=d*y+e*x,C=a*x*x+b*y*y+f*x*y-1;
double t=B*B-4*A*C,z1,z2;
if(t<0) return 1e18;
z1=(-B+sqrt(t))/(2*A),z2=(-B-sqrt(t))/(2*A);
return sqrt(min(x*x+y*y+z1*z1,x*x+y*y+z2*z2));
}
double checky(double x)
{
double l=0,r=3/(2*sqrt(b)),m,lm,rm,templ,tempr,ans=1e18;
while(r-l>eps)
{
lm=l+(r-l)/3;
rm=r-(r-l)/3;
templ=checkz(x,lm);
tempr=checkz(x,rm);
if(templ<=tempr)
{
r=rm;
ans=min(ans,templ);
}
else
l=lm;
}
return ans;
}
int main()
{
while(~scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&e,&f))
{
double l=0,r=3/(2*sqrt(a)),lm,rm,ans=1e18,templ,tempr;
while(r-l>eps)
{
lm=l+(r-l)/3;
rm=r-(r-l)/3;
templ=checky(lm);
tempr=checky(rm);
if(templ<=tempr)
{
r=rm;
ans=min(ans,templ);
}
else
l=lm;
}
printf("%.7f\n",ans);
}
return 0;
}
2.模擬退火
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
using namespace std;
const double eps=1e-9;
int nex[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
double a,b,c,d,e,f;
double dis(double xx,double yy)
{
double aa=c,bb=d*yy+e*xx,cc=f*xx*yy+a*xx*xx+b*yy*yy-1;
double dd=bb*bb-4*aa*cc;
if(dd<0) return -1;
double z1=(-bb+sqrt(dd))/(2*aa);
double z2=(-bb-sqrt(dd))/(2*aa);
return sqrt(xx*xx+yy*yy+min(z1*z1,z2*z2));
}
int main(void)
{
while(~scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&e,&f))
{
double xx,yy,ans,x=sqrt(1.0/a),y=0,t=1;
while(t>eps)
{
ans=dis(x,y);
int i;
for(i=0;i<4;i++)
{
xx=x+t*nex[i][0];
yy=y+t*nex[i][1];
if(dis(xx,yy)<0)
continue;
if(ans-dis(xx,yy)>eps)
break;
}
if(i!=4)
{
x=xx;
y=yy;
}
else
t*=0.66;
}
printf("%.7f\n",ans);
}
return 0;
}