POJ_1375
这个题目可以先用解析几何的方法求出每个圆的两条切线并得到阴影部分,最后再将阴影部分合并即可。
在合并阴影的时候,为了处理起来方便,可以先按左端点排序,这时可以从后往前枚举每个区间,然后从这个区间开始向前查找,如果前面某个区间和这个区间有交集,就删掉这个区间,并将前面那个区间的范围更新成两个区间的并集,这样做是O(n^2)的复杂度。
更好的办法就是首先将第一个区间的左端点赋给x,右端点赋给y,然后依次向后扫描区间,如果当前区间的左端点比y大就输出记录的x、y,并将x、y更新成当前区间的左右端点的值,如果当前区间的左端点不比y大,那么就将y更新成当前区间的右端点及y两者的较大值,最后的时候再输出一下x、y,这样做是O(n) 的复杂度(只是指合并区间的这一环节的复杂度,前面对区间进行排序的复杂度是O(nlogn))。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define zero 1e-8
#define MAXD 510
int N, r[MAXD];
double bx, by, cx[MAXD], cy[MAXD], cr[MAXD], lx[MAXD], rx[MAXD];
int cmp(const void *_p, const void *_q)
{
int *p = (int *)_p;
int *q = (int *)_q;
return lx[*p] < lx[*q] ? -1 : 1;
}
double max(double x, double y)
{
return x > y ? x : y;
}
int dcmp(double x)
{
if(fabs(x) < zero)
return 0;
if(x < 0)
return -1;
return 1;
}
void init()
{
int i, j, k;
scanf("%lf%lf", &bx, &by);
for(i = 0; i < N; i ++)
scanf("%lf%lf%lf", &cx[i], &cy[i], &cr[i]);
}
void solve()
{
int i, j;
double k1, k2, a1, a2, a3, x, y;
for(i = 0; i < N; i ++)
{
a1 = (cx[i] - bx) * (cx[i] - bx) - cr[i] * cr[i];
a2 = 2 * (by - cy[i]) * (cx[i] - bx);
a3 = (cy[i] - by) * (cy[i] - by) - cr[i] * cr[i];
if(dcmp(a1) == 0)
{
k1 = (-a3) / a2;
if(k1 < 0)
lx[i] = bx, rx[i] = (-by) / k1 + bx;
else
lx[i] = (-by) / k1 + bx, rx[i] = bx;
}
else
{
k1 = (-a2 - sqrt(a2 * a2 - 4 * a1 * a3)) / (2 * a1);
k2 = (-a2 + sqrt(a2 * a2 - 4 * a1 * a3)) / (2 * a1);
lx[i] = (-by) / k1 + bx, rx[i] = (-by) / k2 + bx;
}
}
for(i = 0; i < N; i ++)
r[i] = i;
qsort(r, N, sizeof(r[0]), cmp);
x = lx[r[0]], y = rx[r[0]];
for(i = 1; i < N; i ++)
{
if(dcmp(lx[r[i]] - y) > 0)
{
printf("%.2lf %.2lf\n", x, y);
x = lx[r[i]], y = rx[r[i]];
}
else
y = max(y, rx[r[i]]);
}
printf("%.2lf %.2lf\n", x, y);
}
int main()
{
for(;;)
{
scanf("%d", &N);
if(!N)
break;
init();
solve();
printf("\n");
}
return 0;
}