zoj 2589 Circles

好久没做zoj了,一做就是WA一版啊啊啊啊啊!!!

这个题,给你N个圆,求相交后把平面分成了多少份。

我开始找规律的,找不太对,后来一搜,发现是欧拉定理,真无语了。刚学完离散,没想到真能用得上。

不过这个找边数和面数很纠结啊。

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

所以需要转换下。可以证得,如果一个图中有X个连通的平面图,那么满足,V - E + R = X+1。(某张离散卷子考试题T T ,只需要将每个连通分支按题意累和,再考虑最后重叠的最外面即可)

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

没想到好方法,最水的方法,就是两两相交求交点(包括切点),因为可能这些点会有重复的,比如三个圆的交点重合了三个等等,所以需要排除重合的点,我直接用快排然后筛掉想同的点。然后判断这些点(每个点都不同)在多少个圆上。画图可知,一个圆上有N个点,那么这个圆被分割成N条边,所以只要判断这些点在多少个圆上即可。


P.S.:因为这题,学了好几个圆的函数,收获还是不小滴,就是这代码有点长了。。。


#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) 
{
	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)
{
	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)
{
	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)
{
	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)
{
	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)
{
	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);
			}
		ans = ee - nn + sum + 1;
		
		printf("%d\n",ans);
	}
return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值