SCUD Busters

#程序员如何平衡日常编码工作与提升式学习?#

题目描述

PDF

输入格式

输出格式

输入输出样例

输入 #1复制

12
3 3
4 6
4 11
4 8
10 6
5 7
6 6
6 3
7 9
10 4
10 9
1 7
5
20 20
20 40
40 20
40 40
30 30
3
10 10
21 10
21 13
-1
5 5
20 12

输出 #1复制

70.50

题目大意:(自己翻译的)

给你一堆城市,每个城市由一堆点构成,城市的围墙是包含这个城市所有点的最小多边形,城市之间两两不相交。然后有给出一堆导弹的坐标,若一个导弹打在某个城市内部即可摧毁这个城市,问导弹摧毁的城市的总面积。

这是一道运用面积算法的计算几何题。

对于给定的炮台坐标和攻击半径 D,题目要求计算平面内有多少点不能被该炮台中心覆盖到,则可以使用面积差计算得出。因为该问题较难分别处理不同半径情况,所以建议所有炮台都瞄准同样的半径进行计算。

具体来说,我们可以利用每个圆的面积减去它们之间重叠区域的面积以求得最终答案。

操作步骤如下:

  • 首先,定义一个常量 PI 表示圆周率 π。利用该常量与圆形半径进行面积计算。
  • 对于相邻两个圆,若相交,就需要计算出这两个圆重叠段的面积,也就是求出图中的深色部分的面积。
  • 所有的嵌套在大圆内的小圆面积相加是求得的全部被覆盖的区域,即炮台能够覆盖的面积。
  • 最终剩余的面积表示炮台不能覆盖(或者只有部分覆盖)的区域。
  • 注意由于测试用例不是很严格,不一定正确,所以在编写代码时可能需要做出更多的判断(例如半径为负数等异常情况),以避免意外情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const double EPS = 1e-8, pi = acos(-1);

struct Point {
    double x, y;
    Point(double _x = 0, double _y = 0) : x(_x), y(_y) {}
    Point operator+(Point v) { return { x + v.x, y + v.y }; }
    Point operator-(Point v) { return { x - v.x, y - v.y }; }

    double operator*(Point v) { return x * v.y - y * v.x; } // 叉积
};

int n;
Point a[105];
double r[105];

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

double sector(double R, double angle) {
    return R * R * (angle - sin(angle)) / 2.0;
}

double solve(int i, int j) {
    if (dist(a[i], a[j]) <= r[i] - r[j] + EPS) {  // 包含关系
        return pi * r[j] * r[j];
    } else if (dist(a[i], a[j]) >= r[i] + r[j] - EPS) {  // 两圆外离
        return 0;
    } else {
        double d = dist(a[i], a[j]);
        double a1 = acos((r[i] * r[i] + d * d - r[j] * r[j]) / (2.0 * r[i] * d));
        double a2 = acos((r[j] * r[j] + d * d - r[i] * r[i]) / (2.0 * r[j] * d));
        double area1 = sector(r[i], a1) - 0.5 * r[i] * r[i] * sin(2 * a1);
        double area2 = sector(r[j], a2) - 0.5 * r[j] * r[j] * sin(2 * a2);
        return area1 + area2;
    }
}

double check(double R) {  // 计算半径为R的超圆和几何图形的部分面积
    double ans = 0, curr;
    for (int i = 0; i < n; i++) {
        curr = pi * r[i] * r[i];
        for (int j = 0; j < i; j++) {
            curr -= solve(i, j);   //减去与其他圆被覆盖的面积 
        }
        ans += ((i & 1)?-1:1) * curr;  // 奇数个相减,偶数个相加
    }
    return ans;
}

int main() {
    while (cin >> n && n != -1) {
        for (int i = 0; i < n; i++) {
            cin >> a[i].x >> a[i].y >> r[i];
        }
        double L = 0, R = 3000, mid;
        while (R - L >= EPS) {
            mid = (L + R) / 2.0;
            if (check(mid) < 0) L = mid;
            else R = mid;
        }
        cout << fixed << setprecision(2) << R << endl;
    }

    return 0;
}

 0 

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值