uva 1308 - Viva Confetti
这个题目的方法是将圆盘分成一个个圆环,然后判断这些圆环是否被上面的圆覆盖;
如果这个圆的圆周上的圆弧都被上面的覆盖,暂时把它标记为不可见;
然后如果他的头上有个圆,他有个圆弧可见,那么他自己本身可见,并且可以把这条圆弧下面的第一个圆重新标记为可见;
另外,圆弧可见还是不可见利用它的中点来进行判断;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cmath> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 7 using namespace std; 8 9 const int maxn = 100 + 10; 10 const double eps = 1e-14; //别开太大,样例数据就到达1e-11级别 11 const double pi = acos(-1); 12 13 int dcmp(double x) 14 { 15 return fabs(x) < eps ? 0 : (x > 0 ? 1 : -1); 16 } 17 18 struct Point 19 { 20 double x; 21 double y; 22 23 Point(double x = 0, double y = 0):x(x), y(y) {} 24 25 bool operator < (const Point& e) const 26 { 27 return dcmp(x - e.x) < 0 || (dcmp(x - e.x) == 0 && dcmp(y - e.y) < 0); 28 } 29 30 bool operator == (const Point& e) const 31 { 32 return dcmp(x - e.x) == 0 && dcmp(y - e.y) == 0; 33 } 34 35 int read() 36 { 37 return scanf("%lf%lf", &x, &y); 38 } 39 }; 40 41 typedef Point Vector; 42 43 Vector operator + (Point A, Point B) 44 { 45 return Vector(A.x + B.x, A.y + B.y); 46 } 47 48 Vector operator - (Point A, Point B) 49 { 50 return Vector(A.x - B.x, A.y - B.y); 51 } 52 53 Vector operator * (Point A, double p) 54 { 55 return Vector(A.x * p, A.y * p); 56 } 57 58 Vector operator / (Point A, double p) 59 { 60 return Vector(A.x / p, A.y / p); 61 } 62 63 struct Circle 64 { 65 Point c; 66 double r; 67 68 Circle() {} 69 Circle(Point c, double r):c(c), r(r) {} 70 71 int read() 72 { 73 return scanf("%lf%lf%lf", &c.x, &c.y, &r); 74 } 75 76 Point point(double a) 77 { 78 return Point(c.x + r * cos(a), c.y + r * sin(a)); 79 } 80 }; 81 82 double Dot(Vector A, Vector B) 83 { 84 return A.x * B.x + A.y * B.y; 85 } 86 87 double Length(Vector A) 88 { 89 return sqrt(Dot(A, A)); 90 } 91 92 double angle(Vector v) //求向量的极角 93 { 94 return atan2(v.y, v.x); 95 } 96 97 bool PointInCircle(Point p, Circle C) //判断点是否在圆内 98 { 99 double dist = Length(p - C.c); 100 if(dcmp(dist - C.r) > 0) return 0; //这里我选择点在圆边上不算在圆内 101 else return 1; 102 } 103 104 bool CircleInCircle(Circle A, Circle B) //判断圆在圆内 105 { 106 double cdist = Length(A.c - B.c); 107 double rdiff = B.r - A.r; 108 if(dcmp(A.r - B.r) <= 0 && dcmp(cdist - rdiff) <= 0) return 1; //包括重合,内切和内含的情况 109 return 0; 110 } 111 112 int n; 113 Circle C[maxn]; 114 bool vis[maxn]; 115 vector<double> pointAng[maxn]; 116 117 int GetCircleCircleIntersection(int c1, int c2) //求圆与圆的交点 118 { 119 Circle C1 = C[c1]; 120 Circle C2 = C[c2]; 121 double d = Length(C1.c - C2.c); 122 if(dcmp(d) == 0) 123 { 124 if(dcmp(C1.r - C2.r) == 0) return -1; //两圆重合 125 return 0; //同心圆但不重合 126 } 127 if(dcmp(C1.r + C2.r - d) < 0) return 0; //外离 128 if(dcmp(fabs(C1.r - C2.r) - d) > 0) return 0; //内含 129 double a = angle(C2.c - C1.c); 130 double da = acos((C1.r * C1.r + d * d - C2.r * C2.r) / (2 * C1.r * d)); 131 Point p1 = C1.point(a + da); 132 Point p2 = C1.point(a - da); 133 if(p1 == p2) return 1; //相切 134 pointAng[c1].push_back(a + da); //相切的点不处理,只要相交的 135 pointAng[c1].push_back(a - da); 136 return 2; 137 } 138 139 void init() 140 { 141 for(int i = 0; i < n; i++) pointAng[i].clear(); 142 memset(vis, 0, sizeof(vis)); 143 } 144 145 void read() 146 { 147 for(int i = 0; i < n; i++) C[i].read(); 148 } 149 150 void solve() 151 { 152 for(int i = 0; i < n; i++) //圆两两相交,得各圆交点集合 153 for(int j = 0; j < n; j++) if(i != j) 154 GetCircleCircleIntersection(i, j); 155 for(int i = 0; i < n; i++) 156 { 157 sort(pointAng[i].begin(), pointAng[i].end()); //各圆交点按极角排序 158 vector<double>::iterator iter = unique(pointAng[i].begin(), pointAng[i].end()); //去重,可减少运行时间,不去重也能AC 159 pointAng[i].resize(distance(pointAng[i].begin(), iter)); 160 } 161 for(int i = 0; i < n; i++) //判断第i个圆上的弧 162 { 163 int sz = pointAng[i].size(); 164 if(!sz) //此圆不与其他圆相交 165 { 166 bool ok = 1; 167 for(int k = i+1; k < n; k++) if(CircleInCircle(C[i], C[k])) //判上面是否有圆把它覆盖掉 168 { 169 ok = 0; 170 break; 171 } 172 if(ok) vis[i] = 1; 173 } 174 else 175 { 176 pointAng[i].push_back(pointAng[i][0]); 177 for(int j = 0; j < sz; j++) //第i个圆上的第j条弧 178 { 179 bool ok = 1; 180 Point pm = C[i].point((pointAng[i][j] + pointAng[i][j+1]) / 2); //取弧的中点 181 for(int k = i+1; k < n; k++) if(PointInCircle(pm, C[k])) 182 { 183 ok = 0; 184 break; 185 } 186 if(ok) 187 { 188 vis[i] = 1; 189 for(int u = i-1; u >= 0; u--)if(PointInCircle(pm, C[u])) //把这段圆弧下的圆设为可见 190 { 191 vis[u] = 1; 192 break; 193 } 194 } 195 } 196 } 197 } 198 int ret = 0; 199 for(int i = 0; i < n; i++) if(vis[i]) ret++; 200 printf("%d\n", ret); 201 } 202 203 int main() 204 { 205 while(scanf("%d", &n) == 1 && n) 206 { 207 init(); 208 read(); 209 solve(); 210 } 211 return 0; 212 }