LA - 2572 - Viva Confetti

来源:互联网 发布:幻想神域软件 编辑:程序博客网 时间:2024/05/01 08:29

题意:先后给出n个圆,问最后能看到多少个圆(n <= 100,输入数据小数位数可达12位)。

题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=573

——>>圆两两相交,得到交点(保存其极角就行),每个圆内交点的极角排序,扫描每一小段弧,判断其弧中点(不是弦中点)是否在上面的圆内,若这段小弧可见,再将其能看到的下面的第一个圆设为可见。

注意:精度eps不能太大。

#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int maxn = 100 + 10;const double eps = 1e-14;        //别开太大,样例数据就到达1e-11级别const double pi = acos(-1);int dcmp(double x){    return fabs(x) < eps ? 0 : (x > 0 ? 1 : -1);}struct Point{    double x;    double y;    Point(double x = 0, double y = 0):x(x), y(y){}    bool operator < (const Point& e) const{        return dcmp(x - e.x) < 0 || (dcmp(x - e.x) == 0 && dcmp(y - e.y) < 0);    }    bool operator == (const Point& e) const{        return dcmp(x - e.x) == 0 && dcmp(y - e.y) == 0;    }    int read(){        return scanf("%lf%lf", &x, &y);    }};typedef Point Vector;Vector operator + (Point A, Point B){    return Vector(A.x + B.x, A.y + B.y);}Vector operator - (Point A, Point B){    return Vector(A.x - B.x, A.y - B.y);}Vector operator * (Point A, double p){    return Vector(A.x * p, A.y * p);}Vector operator / (Point A, double p){    return Vector(A.x / p, A.y / p);}struct Circle{    Point c;    double r;    Circle(){}    Circle(Point c, double r):c(c), r(r){}    int read(){        return scanf("%lf%lf%lf", &c.x, &c.y, &r);    }    Point point(double a){        return Point(c.x + r * cos(a), c.y + r * sin(a));    }};double Dot(Vector A, Vector B){    return A.x * B.x + A.y * B.y;}double Length(Vector A){    return sqrt(Dot(A, A));}double angle(Vector v){     //求向量的极角    return atan2(v.y, v.x);}bool PointInCircle(Point p, Circle C){      //判断点是否在圆内    double dist = Length(p - C.c);    if(dcmp(dist - C.r) > 0) return 0;      //这里我选择点在圆边上不算在圆内    else return 1;}bool CircleInCircle(Circle A, Circle B){        //判断圆在圆内    double cdist = Length(A.c - B.c);    double rdiff = B.r - A.r;    if(dcmp(A.r - B.r) <= 0 && dcmp(cdist - rdiff) <= 0) return 1;      //包括重合,内切和内含的情况    return 0;}int n;Circle C[maxn];bool vis[maxn];vector<double> pointAng[maxn];int GetCircleCircleIntersection(int c1, int c2){      //求圆与圆的交点    Circle C1 = C[c1];    Circle C2 = C[c2];    double d = Length(C1.c - C2.c);    if(dcmp(d) == 0){        if(dcmp(C1.r - C2.r) == 0) return -1;       //两圆重合        return 0;       //同心圆但不重合    }    if(dcmp(C1.r + C2.r - d) < 0) return 0;     //外离    if(dcmp(fabs(C1.r - C2.r) - d) > 0) return 0;       //内含    double a = angle(C2.c - C1.c);    double da = acos((C1.r * C1.r + d * d - C2.r * C2.r) / (2 * C1.r * d));    Point p1 = C1.point(a + da);    Point p2 = C1.point(a - da);    if(p1 == p2) return 1;      //相切    pointAng[c1].push_back(a + da);     //相切的点不处理,只要相交的    pointAng[c1].push_back(a - da);    return 2;}void init(){    for(int i = 0; i < n; i++) pointAng[i].clear();    memset(vis, 0, sizeof(vis));}void read(){    for(int i = 0; i < n; i++) C[i].read();}void solve(){    for(int i = 0; i < n; i++)      //圆两两相交,得各圆交点集合        for(int j = 0; j < n; j++) if(i != j)            GetCircleCircleIntersection(i, j);    for(int i = 0; i < n; i++){        sort(pointAng[i].begin(), pointAng[i].end());       //各圆交点按极角排序        vector<double>::iterator iter = unique(pointAng[i].begin(), pointAng[i].end());     //去重,可减少运行时间,不去重也能AC        pointAng[i].resize(distance(pointAng[i].begin(), iter));    }    for(int i = 0; i < n; i++){     //判断第i个圆上的弧        int sz = pointAng[i].size();        if(!sz){        //此圆不与其他圆相交            bool ok = 1;            for(int k = i+1; k < n; k++) if(CircleInCircle(C[i], C[k])){        //判上面是否有圆把它覆盖掉                ok = 0;                break;            }            if(ok) vis[i] = 1;        }        else{            pointAng[i].push_back(pointAng[i][0]);            for(int j = 0; j < sz; j++){        //第i个圆上的第j条弧                bool ok = 1;                Point pm = C[i].point((pointAng[i][j] + pointAng[i][j+1]) / 2);     //取弧的中点                for(int k = i+1; k < n; k++) if(PointInCircle(pm, C[k])){                    ok = 0;                    break;                }                if(ok){                    vis[i] = 1;                    for(int u = i-1; u >= 0; u--)if(PointInCircle(pm, C[u])){       //把这段圆弧下的圆设为可见                        vis[u] = 1;                        break;                    }                }            }        }    }    int ret = 0;    for(int i = 0; i < n; i++) if(vis[i]) ret++;    printf("%d\n", ret);}int main(){   //freopen("data.txt", "r", stdin);      //提交时忘了注释掉这个吃了2个WA。。。    while(scanf("%d", &n) == 1 && n){        init();        read();        solve();    }    return 0;}