I. Rise of the Robots(最小圆覆盖)

I. Rise of the Robots

题目链接

题目大意:在一个以 ( 0 , 0 ) (0, 0) (0,0) 为圆心 R R R 为半径的圆桌上要举行一场比赛,一个圆形机器人自身半径为 r r r ,他有 n n n 步行动,起点未知,给出每一步的 ( d x , d y ) (d_x, d_y) (dx,dy) ,设机器人当前在 ( x , y ) (x, y) (x,y) ,则下一步行动走到的点为 ( x + d x , y + d y ) (x + d_x, y + d_y) (x+dx,y+dy) ,现在问将起点设在哪里可以保证这个机器人在行动的全程自身边界不会超过圆桌,题目保证有解。

题目看似好像得找一个最小圆去覆盖每一个圆,然而其实我们可以发现,将这个覆盖的圆半径缩小 r r r 就正好是这些点最小圆覆盖。我们可以先假设机器人行动的起点在 ( 0 , 0 ) (0,0) (0,0) ,然后求这 n + 1 n+1 n+1 个点最小圆覆盖,求出圆心坐标 ( x , y ) (x, y) (x,y) 。因为题目保证了有解,所以我们将这个圆平移到圆心为 ( 0 , 0 ) (0, 0) (0,0) 的位置肯定是最优的(如果这里都不满足条件就没有地方满足条件了),因为我们这里相当于把最小覆盖圆挪到了圆心位置,那么我们自然就要把所有点移动同样的距离,也就是将起点移动同样的距离,即最终的起点答案为 ( − x , − y ) (-x, -y) (x,y)

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 2;
const double eps = 1e-11;

int n;
double R, r;
struct Dot {
    double x;
    double y;
}dot[N];
struct Cir {
    Dot o;
    double r;
}ans;

double dis(double x1, double y1, double x2, double y2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

Cir getr(double x1, double y1, double x2, double y2, double x3, double y3) {//三个点求三角形圆心坐标和半径
    Cir temp;
    temp.o.x = ((y2-y1)*(y3*y3-y1*y1+x3*x3-x1*x1)-(y3-y1)*(y2*y2-y1*y1+x2*x2-x1*x1))/(2.0*((x3-x1)*(y2-y1)-(x2-x1)*(y3-y1)));
    temp.o.y = ((x2-x1)*(x3*x3-x1*x1+y3*y3-y1*y1)-(x3-x1)*(x2*x2-x1*x1+y2*y2-y1*y1))/(2.0*((y3-y1)*(x2-x1)-(y2-y1)*(x3-x1)));
    temp.r = dis(temp.o.x, temp.o.y, x1, y1);
    return temp;
}

bool inCir(Dot t) {
    double check = (t.x - ans.o.x) * (t.x - ans.o.x) + (t.y - ans.o.y) * (t.y - ans.o.y);
    return (check < ans.r * ans.r || fabs(check - ans.r * ans.r) < eps);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    freopen("robots.in", "r", stdin);
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%lf%lf", &n, &R, &r);
        dot[0].x = 0, dot[0].y = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%lf%lf", &dot[i].x, &dot[i].y);
            dot[i].x += dot[i - 1].x, dot[i].y += dot[i - 1].y;
        }
        random_shuffle(dot, dot + n + 1);
        ans.o.x = dot[0].x, ans.o.y = dot[0].y, ans.r = 0;
        for (int i = 1; i <= n; ++i) {
            if (inCir(dot[i])) continue;
            ans.o.x = dot[i].x, ans.o.y = dot[i].y, ans.r = 0;
            for (int j = 0; j < i; ++j) {
                if (inCir(dot[j])) continue;
                ans.o.x = (dot[i].x + dot[j].x) / 2;
                ans.o.y = (dot[i].y + dot[j].y) / 2;
                ans.r = dis(dot[i].x, dot[i].y, dot[j].x, dot[j].y) / 2;
                for (int k = 0; k < j; ++k) {
                    if (inCir(dot[k])) continue;
                    ans = getr(dot[i].x, dot[i].y, dot[j].x, dot[j].y, dot[k].x, dot[k].y);
                }
            }
        }
        printf("%.9f %.9f\n", 0-ans.o.x, 0-ans.o.y);
    }
}

最小圆覆盖

模板题目

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 2;
const double eps = 1e-11;

int n;
struct Dot {
    double x;
    double y;
}dot[N];
struct Cir {
    Dot o;
    double r;
}ans;

double dis(double x1, double y1, double x2, double y2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

Cir getr(double x1, double y1, double x2, double y2, double x3, double y3) {//三个点求三角形圆心坐标和半径
    Cir temp;
    temp.o.x = ((y2-y1)*(y3*y3-y1*y1+x3*x3-x1*x1)-(y3-y1)*(y2*y2-y1*y1+x2*x2-x1*x1))/(2.0*((x3-x1)*(y2-y1)-(x2-x1)*(y3-y1)));
    temp.o.y = ((x2-x1)*(x3*x3-x1*x1+y3*y3-y1*y1)-(x3-x1)*(x2*x2-x1*x1+y2*y2-y1*y1))/(2.0*((y3-y1)*(x2-x1)-(y2-y1)*(x3-x1)));
    temp.r = dis(temp.o.x, temp.o.y, x1, y1);
    return temp;
}

bool inCir(Dot t) {
    double check = (t.x - ans.o.x) * (t.x - ans.o.x) + (t.y - ans.o.y) * (t.y - ans.o.y);
    return (check < ans.r * ans.r || fabs(check - ans.r * ans.r) < eps);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%lf%lf", &dot[i].x, &dot[i].y);
    }
    random_shuffle(dot, dot + n);
    ans.o.x = dot[0].x, ans.o.y = dot[0].y, ans.r = 0;
    for (int i = 1; i < n; ++i) {
        if (inCir(dot[i])) continue;
        ans.o.x = dot[i].x, ans.o.y = dot[i].y, ans.r = 0;
        for (int j = 0; j < i; ++j) {
            if (inCir(dot[j])) continue;
            ans.o.x = (dot[i].x + dot[j].x) / 2;
            ans.o.y = (dot[i].y + dot[j].y) / 2;
            ans.r = dis(dot[i].x, dot[i].y, dot[j].x, dot[j].y) / 2;
            for (int k = 0; k < j; ++k) {
                if (inCir(dot[k])) continue;
                ans = getr(dot[i].x, dot[i].y, dot[j].x, dot[j].y, dot[k].x, dot[k].y);
            }
        }
    }
    printf("%.10f\n%.10f %.10f", ans.r, ans.o.x, ans.o.y);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值