题意:先后给出n个圆,问最后能看到多少个圆(n <= 100,输入数据小数位数可达12位)。
——>>圆两两相交,得到交点(保存其极角就行),每个圆内交点的极角排序,扫描每一小段弧,判断其弧中点(不是弦中点)是否在上面的圆内,若这段小弧可见,再将其能看到的下面的第一个圆设为可见。
注意:精度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;
}