Viva Confetti UVA - 1308

思路:由于圆与圆相交的可见部分都是有圆弧组成的,枚举一个圆与其他圆的交点,对两交点所形成的圆弧,去判断该圆弧的中间点是否在其他圆内。若都不在,则证明该圆没有被完全遮住。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 100 + 10;
const double eps = 1e-14;
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()
{
    while(scanf("%d", &n) == 1 && n)
    {
        init();
        read();
        solve();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值