[编程|1500分] 神奇盘子
时间限制:C/C++ 1秒,其他语言 2秒
空间限制:C/C++ 262144K,其他语言 524288K
Special Judge,64bit IO Format: %lld
题目描述
有一个神奇的盘子,形状为圆形。盘子上面爬着一个大象(视作一个点)。由于现实的扭曲,当大象在盘子某个直径的一端的时候,可以瞬间传送至直径的另一端。现在大象想去盘子上另外一点,问他最少需要移动多少距离。传送不计距离。
输入描述:
第一行一个整数r(1 <= r <= 1000)代表盘子的大小。
第二行两个整点分别代表大象所在的位置和大象目标的位置。保证两个点都在圆内(可能在边界上),圆心在点(0, 0)上。
输出描述:
输出一个实数,代表大象最短需要移动多少距离。和标程相对或绝对相差1e-6都算正确。
示例1
输入
1
0 1
0 -1
输出
0.000000000000
示例2
输入
4
3 0
-3 0
输出
2.000000000000
说明
示例3
输入
100
-59 76
3 69
输出
62.393909959226
代码 (之前的代码只跑了77.42%,更新了代码)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e6+10;
double esp=1e-6;
double r,x11,y11,x22,y22;
double pi=acos(-1.0);
double dist(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
while(cin>>r>>x11>>y11>>x22>>y22)
{
double ans=dist(x11,y11,x22,y22);
double x,y;
for(double i=0;i<=pi;i+=0.000001)
{
x=r*cos(i),y=r*sin(i);
//cout<<x<<" "<<y<<endl;
ans=min(ans,dist(-x,-y,x11,y11)+dist(x,y,x22,y22));
ans=min(ans,dist(x,y,x11,y11)+dist(-x,-y,x22,y22));
}
printf("%.15f\n",ans);
}
return 0;
}
另附官方题解
思路
把一个点(x,y)翻到(-x,-y),就可以把题意转换为在圆周上求一个点,使得这个点到起点
与终点的距离和最小。直接三分角度或二分导数就可以了。时间复杂度为O(nm)。
#include<bits/stdc++.h>
using namespace std;
typedef long double D;
D pi = acos(-1.);
D eps = 1e-8;
inline int sign(const D & x) {
return (x > eps) - (x + eps < 0);
}
struct P {
D x, y;
void scan() {
double _x, _y;
scanf("%lf%lf", &_x, &_y);
x = _x; y = _y;
}
D sqrlen() const {
return x * x + y * y;
}
D len() const {
return sqrt(max((D)0., sqrlen()));
}
P operator + (const P & b) const {
return P{x + b.x, y + b.y};
}
P operator - (const P & b) const {
return P{x - b.x, y - b.y};
}
D operator % (const P & b) const {
return x * b.x + y * b.y;
}
D operator * (const P & b) const {
return x * b.y - y * b.x;
}
P zoom(const D & l) const {
D lambda(l / len());
return P{lambda * x, lambda * y};
}
void print() const {
printf("%.12f %.12f\n", (double)x, (double)y);
}
};
D atan2(const P & x) { return atan2(x.y, x.x); }
P operator * (const D & x, const P & a) { return P{x * a.x, x * a.y}; }
int main() {
int r;
scanf("%d", &r);
P a, b;
a.scan(); b.scan();
D ans((a - b).len());
b = -1. * b;
if(sign(a * b) == 0) {
ans = min(ans, 2 * r - ans);
}else {
D t1 = atan2(a), t2 = atan2(b);
if(t1 > t2) swap(t1, t2), swap(a, b);
if(t2 - t1 > pi) t1 += pi, swap(t1, t2), swap(a, b);
D le = t1, ri = t2;
for(int i(0); i < 1000; i++) {
D mid((le + ri) / 2);
P p = r * P{cos(mid), sin(mid)};
//p.print();
if(((p - a).zoom(1) - (p - b).zoom(1)) % (p - P{0, 0}) > 0) {
le = mid;
}else ri = mid;
}
P p = r * P{cos(le), sin(le)};
ans = min(ans, (p - a).len() + (p - b).len());
}
printf("%.12f\n", (double)ans);
}