算法竞赛入门经典--训练指南
题目大意:n个圆盘依次放在桌面上,给出每个圆盘的坐标和圆心,求能看见的圆的个数;
分析:圆的每个可见部分由小圆弧围成,因此可以先求出所有小圆弧,然后判断每段小圆弧内外两侧的可见圆盘.具体来说,把小圆弧中点往内外两侧各移动很小距离,得到两个点,然后标记包含这两个点的圆盘中最顶部的那个为可见的;
算法实现:离散化求出与一个圆的所有交点的弧度,排序后, 两个相邻的交点之间 只有 一个 弧,在求这个弧的中点,分别向内外移动,判断
CODE:
#include <iostream> #include <cmath> #include <algorithm> #include <cstdio> using namespace std; const int MAX_N = 128; const double EPS = 5e-13; const double PI = acos(-1.0); typedef struct { double x, y; } point; //两点距离 double Distance(const point & p1, const point & p2); //如果一个角大于360,减去360,小于0加上360 double MainAngle(double a); int n; //number of circles point o[MAX_N]; //圆心 double r[MAX_N]; //圆的弧度 int pan; //与这个圆的交点数目 double pa[2 * MAX_N]; //存放与这个圆所有交点对应的弧度 int visible[MAX_N]; //是否被访问过 int ans; //answer int main() { int i, j, k, t; point tp; double a, b, d; while (scanf("%d", &n), n) { for (i = 0; i < n; ++i) { scanf("%lf %lf %lf", &o[i].x, &o[i].y, &r[i]); visible[i] = 0; } for (i = 0; i < n; ++i) { pan = 0; pa[pan++] = 0; pa[pan++] = 2 * PI; for (j = 0; j < n; ++j) { if (j == i) { continue; } d = Distance(o[i], o[j]); //判断两个圆心距离 if (r[i] + r[j] < d || r[i] + d < r[j] || r[j] + d < r[i]) //包含或不相交的 { continue; } a = atan2(o[j].y - o[i].y, o[j].x - o[i].x);//atan2(),是求这个点和x轴正方形夹角,*PI/180 得到度数 b = acos((r[i] * r[i] + d * d - r[j] * r[j]) / (2 * r[i] * d)); pa[pan] = MainAngle(a + b); pan++; pa[pan] = MainAngle(a - b); pan++; } sort(pa, pa + pan); for (j = 0; j < pan - 1; ++j) { a = (pa[j] + pa[j + 1]) / 2; for (t = -1; t < 2; t += 2) //t = -1 或 1 { //将每段圆弧中点往内外各移动很小距离 tp.x = o[i].x + (r[i] + t * EPS) * cos(a); tp.y = o[i].y + (r[i] + t * EPS) * sin(a); for (k = n - 1; k >= 0; --k) { //如果找到第一个cover point i 的 Arc j 的圆,break if (Distance(tp, o[k]) < r[k]) { break; } } visible[k] = 1; } } } ans = 0; for (i = 0; i < n; ++i) if (visible[i] == 1) { ans++; } printf("%d\n", ans); } return 0; } double Distance(const point & p1, const point & p2) { return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); } double MainAngle(double a) { while (a > 2 * PI) { a -= 2 * PI; } while (a < 0) { a += 2 * PI; } return a; }