NC16746 神奇盘子

题目

题目描述

有一个神奇的盘子,形状为圆形。盘子上面爬着一个大象(视作一个点)。由于现实的扭曲,当大象在盘子某个直径的一端的时候,可以瞬间传送至直径的另一端。现在大象想去盘子上另外一点,问他最少需要移动多少距离。传送不计距离。

输入描述

第一行一个整数r(1 <= r <= 1000)代表盘子的半径。
接下来两行两个整点分别代表大象所在的位置和大象目标的位置坐标。保证两个点都在圆内(可能在边界上),圆心在点(0, 0)上。

输出描述

输出一个实数,代表大象最短需要移动多少距离。和标程相对或绝对相差1e-6都算正确。

示例1

输入

1
0 1
0 -1

输出

0.000000000000

示例2

输入

4
3 0
-3 0

输出

2.000000000000

说明

img

示例3

输入

100
-59 76
3 69

输出

62.393909959226

题解

知识点:计算几何,三分。

可以知道圆周上有一极小点使得通过传送的方法后距离是最短的,用这个最小值,与直接走过去对比取最小。

对弧度三分 \(l = 0,r = 2\pi\) ,用参数方程表示出圆周上点坐标,随后计算两个三分点的各自距离之和,然后比较舍大区间。

要注意的是误差开大一点。

\(\pi\) 可以通过 \(acos(-1.0)\) 取得较精确的值。

对称点的原弧度加上 \(\pi\) 生成即可。

实数范围 \(l\)\(r\) 都可以,因为都在误差范围。

时间复杂度 \(O(1)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;

const double PI = acos(-1.0);
const double eps = 1e-6;

struct Point {
    double x, y;
}a, b;
double r;

double dist(Point a, Point b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

Point f(double rad) {
    return { r * cos(rad), r * sin(rad) };
}

bool check(double mid1, double mid2) {
    double d1 = dist(a, f(mid1)) + dist(f(mid1 + PI), b);
    double d2 = dist(a, f(mid2)) + dist(f(mid2 + PI), b);
    return d1 <= d2;
}


int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> r >> a.x >> a.y >> b.x >> b.y;
    double ans = dist(a, b);
    double l = 0, r = 2 * PI;
    while (r - l >= eps) {
        double mid1 = l + (r - l) / 3;
        double mid2 = r - (r - l) / 3;
        if (check(mid1, mid2)) r = mid2;
        else l = mid1;
    }
    ans = min(ans, dist(a, f(l)) + dist(f(l + PI), b));
    cout << fixed << setprecision(8) << ans << '\n';
    return 0;
}
posted @ 2022-06-28 23:40  空白菌  阅读(99)  评论(1编辑  收藏  举报