Circles

Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu

[Submit]   [Go Back]   [Status]

Description

Consider N different circles on the plane. They divide it to several parts, you have to find the number of these parts.

For the purpose of this problem the circle of radius r with center (x0, y0) is the set of points


Input

The input contains multiple test cases. The first line of the input is a single integer T (1 <= T <= 20) which is the number of test cases. T test cases follow, each preceded by a single blank line.

The first line of each test case contains N - the number of circles (1 <= N <= 50). Next N lines contain three integer numbers x0, y0, and r each - the coordinates of the center and the radius of the circle. All coordinates do not exceed 103 by their absolute value, all radii are positive and do not exceed 103. No two circles coincide.


Output

For each test case, output K - the number of parts circles divide the plane to, in a single line.

Due to floating point precision losses possible, do not consider parts with area not exceeding 10-10.


Sample Input

2

2
0 0 3
0 0 2

2
0 0 1
2 0 1

Sample Output

3
3

[Submit]   [Go Back]   [Status]

我开始找规律的,一开始以为找对了。。。唉没想到。。队友的一句话。。

原来弄错了。。。后来才知道。。欧拉定理。。唉。。无语啊


根据欧拉定理,连通平面图(圆相交的图一定是平面图)满足 V - E + R = 2。 其中V是顶点数,E是边数,R是面数。这题就是求面数,但是这个不一定是连通的平面图。

所以需要转换下。可以证得,如果一个图中有X个连通的平面图,那么满足,V - E + R = X+1。

所以就转换成求这个图的边数和顶点数。



#include <queue>

#include <stack>

#include <math.h>

#include <stdio.h>

#include <stdlib.h>

#include <iostream>

#include <limits.h>

#include <string.h>

#include <string>

#include <algorithm>


using namespace std;


const int MAX = 51;

struct point{ double x,y;};

struct circle{ point a;double r;};

circle c[MAX];

point p[MAX*MAX];//所有的交点

const double eps = 1e-8;

bool dy(double x,double y)  {   return x > y + eps;} // x > y

bool xy(double x,double y)  {   return x < y - eps;} // x < y

bool dyd(double x,double y) {   return x > y - eps;} // x >= y

bool xyd(double x,double y) {   return x < y + eps;} // x <= y

bool dd(double x,double y)  {   return fabs( x - y ) < eps;}  // x == y

double disp2p(point a,point b)//求点a与点b之间的距离

{

    return sqrt( ( a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) );

}


bool c2c_inst(circle a,circle b)//判断圆 a和圆 b是否相隔

{

    if( xy(disp2p(a.a,b.a),a.r+b.r) && dy(disp2p(a.a,b.a),fabs(a.r - b.r)) )

        return true;

    return false;

}


point c2c_tangent_p(circle a,circle b)

{

    point t;

    if( dd(disp2p(a.a,b.a),a.r+b.r)  )

    {

        t.x = (a.r*b.a.x + b.r*a.a.x)/(a.r + b.r);

        t.y = (a.r*b.a.y + b.r*a.a.y)/(a.r + b.r);

        return t;

    }

    t.x = (a.r*b.a.x - b.r*a.a.x)/(a.r - b.r);

    t.y = (a.r*b.a.y - b.r*a.a.y)/(a.r - b.r);

    return t;

}

bool c2c_tangent(circle a,circle b)//判断两圆是否相切(内切,外切)

{

    if( dd(disp2p(a.a,b.a),a.r+b.r) || dd(disp2p(a.a,b.a),fabs(a.r-b.r)) )

        return true;

    return false;

}

bool cmp(point a,point b)//升序排列

{

    if( dd(a.x,b.x) ) return xy(a.y,b.y);

    return xy(a.x,b.x);

}

point l2l_inst_p(point u1,point u2,point v1,point v2)

{

    point ans = u1;

    double t = ((u1.x - v1.x)*(v1.y - v2.y) - (u1.y - v1.y)*(v1.x - v2.x))/

                ((u1.x - u2.x)*(v1.y - v2.y) - (u1.y - u2.y)*(v1.x - v2.x));

    ans.x += (u2.x - u1.x)*t;

    ans.y += (u2.y - u1.y)*t;

    return ans;

}

void l2c_inst_p(point c,double r,point l1,point l2,point &p1,point &p2)//求直线l1l2与圆相切的两切点

{

    point p = c;

    double t;

    p.x += l1.y - l2.y;

    p.y += l2.x - l1.x;

    p = l2l_inst_p(p,c,l1,l2);

    t = sqrt(r*r - disp2p(p,c)*disp2p(p,c))/disp2p(l1,l2);

    p1.x = p.x + (l2.x - l1.x)*t;

    p1.y = p.y + (l2.y - l1.y)*t;

    p2.x = p.x - (l2.x - l1.x)*t;

    p2.y = p.y - (l2.y - l1.y)*t;

}

void c2c_inst_p(point c1,double r1,point c2,double r2,point &p1,point &p2)//求两个相交圆的交点p1,p2

{

    point u,v;

    double t;

    t = (1 + (r1*r1 - r2*r2)/disp2p(c1,c2)/disp2p(c1,c2))/2;

    u.x = c1.x + (c2.x - c1.x)*t;

    u.y = c1.y + (c2.y - c1.y)*t;

    v.x = u.x + c1.y - c2.y;

    v.y = u.y - c1.x + c2.x;

    l2c_inst_p(c1,r1,u,v,p1,p2);

}

int ee;

void cntee(point v,int n)//求整个平面的边数(因为p[]已经排完序了)

{

    for(int i=0; i<n; i++)

        if( dd((v.x - c[i].a.x)*(v.x - c[i].a.x) + (v.y - c[i].a.y)*(v.y - c[i].a.y),c[i].r*c[i].r) )

            ee++;

}

bool used[MAX];

void DFS(int x,int n)//求联通图的数目  第x个园,共n个圆

{

    for(int i=0; i<n; i++)

        if( !used[i] && ( c2c_inst(c[i],c[x]) || c2c_tangent(c[i],c[x]) ) )

        {

            used[i] = true;

            DFS(i,n);

        }

}

int main()

{

    int n,ncases;

    scanf("%d",&ncases);


    while( ncases-- )

    {

        scanf("%d",&n);

        for(int i=0; i<n; i++)

            scanf("%lf%lf%lf",&c[i].a.x,&c[i].a.y,&c[i].r);


        int ans,cnt = 0,sum = 0;

        ee = 0;

        memset(used,false,sizeof(used));


        for(int i=0; i<n; i++)

            if( !used[i] )

            {

                used[i] = true;

                sum++;

                DFS(i,n);

            }


        for(int i=0; i<n; i++)

            for(int k=i+1; k<n; k++)

            {

                if( c2c_tangent(c[i],c[k]) )

                    p[cnt++] = c2c_tangent_p(c[i],c[k]);

                if( c2c_inst(c[i],c[k]) )

                    c2c_inst_p(c[i].a,c[i].r,c[k].a,c[k].r,p[cnt++],p[cnt++]);

            }


        sort(p,p+cnt,cmp);


        int nn = 0;//边数

        if( cnt )

        {

            cntee(p[0],n);//单独求第一个交点所在圆的边数

            nn = 1;

        }


        for(int i=1; i<cnt; i++)

            if( !dd(p[i].x,p[i-1].x) || !dd(p[i].y,p[i-1].y) )

            {

                nn++;//求实际(不重复的)点数

                cntee(p[i],n);//求第2...n个交点所在的圆的边数(因为一个交点即一个边数)

            }

        ans = ee - nn + sum + 1;//欧拉定理 V - E + R = X + 1  所以 R = E(ee) - V(nn) + X(sum) +1;


        printf("%d\n",ans);

    }

return 0;

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值