您的位置:首页 > 其它

POJ 1375 Intervals【过定点做圆的切线】

2013-08-23 11:30 381 查看

链接:

http://poj.org/problem?id=1375

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=29328#problem/G

Intervals

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 3675 Accepted: 1056
Description

In the ceiling in the basement of a newly open developers building a light source has been installed. Unfortunately, the material used to cover the floor is very sensitive to light. It turned out that its expected life time is decreasing dramatically. To avoid
this, authorities have decided to protect light sensitive areas from strong light by covering them. The solution was not very easy because, as it is common, in the basement there are different pipelines under the ceiling and the authorities want to install
the covers just on those parts of the floor that are not shielded from the light by pipes. To cope with the situation, the first decision was to simplify the real situation and, instead of solving the problem in 3D space, to construct a 2D model first. 

Within this model, the x-axis has been aligned with the level of the floor. The light is considered to be a point light source with integer co-ordinates [bx,by]. The pipes are represented by circles. The center of the circle i has the integer co-ordinates [cxi,cyi]
and an integer radius ri. As pipes are made from solid material, circles cannot overlap. Pipes cannot reflect the light and the light cannot go through the pipes. You have to write a program which will determine the non-overlapping intervals on the x-axis
where there is, due to the pipes, no light from the light source. 



Input

The input consists of blocks of lines, each of which except the last describes one situation in the basement. The first line of each block contains a positive integer number N < 500 expressing the number of pipes. The second line of the block contains two integers
bx and by separated by one space. Each of the next N lines of the block contains integers cxi, cyi and ri, where cyi + ri < by. Integers in individual lines are separated by one space. The last block consists of one line containing n = 0.
Output

The output consists of blocks of lines, corresponding to the blocks in the input(except the last one). One empty line must be put after each block in the output. Each of the individual lines of the blocks in the output will contain two real numbers, the endpoints
of the interval where there is no light from the given point light source. The reals are exact to two decimal places and separated by one space. The intervals are sorted according to increasing x-coordinate.
Sample Input
6
300 450
70 50 30
120 20 20
270 40 10
250 85 20
220 30 30
380 100 100
1
300 300
300 150 90
1
300 300
390 150 90
0

Sample Output
0.72 78.86
88.50 133.94
181.04 549.93

75.00 525.00

300.00 862.50

Source

Central Europe 1996

算法: 计算几何  定点做圆的切线 向量的旋转

题意:

天花板上有一个点, 下面有 N 个圆
过天花板上的这个点,做这些圆的切线,那么就会在地板上留下阴影部分
按从左到右输出所有的阴影部分。
注意: 要合并重叠的
           cyi + ri < by 圆一定在天花板下面.

思路:

由题目可以知道, 圆一定在天花板下面, 所以说,定点一定能对每个圆画出两条切线.
那么我们只要确定了切线,这条直线和 X 轴的交点还是很好确立的。

如何确立切线

1.先求出定点 Ceil 到圆心 C 的方向向量 U .【已知两个确定的点求方向 U = C - Ceil】
2.再根据圆的切线性质求出:切线相对于 U 的偏转角度 a 【sin(a) = r / Length(u)】不懂的自己画图很容易理解的。。。
                                                   从而可以得到角度 a 的弧度值 用反三角函数 asin() 即可。
3.令 2 中求出的弧度值为 ang 
   那么方向向量 U 顺时针旋转 rad 就可以得到第一条切线,确立阴影的起始位置
   逆时针旋转 rad 就可以得到第二条切线, 确立阴影的结束位置。
   已知一条直线的方向和线上一定点 , 求其与 X 轴的交点还是很好处理的吧。

关于向量的旋转这里套用向量旋转公式:证明见:点击打开链接
//rad是弧度
//向量 A 逆时针旋转 rad
Vector Rotate(Vector A, double rad) {
return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}

已知一条直线的方向 V 和线上一定点 P  , 求其与 X 轴的交点

个人理解还是根据点斜式处理比较好了,但是这里的斜率不是  K = V.y / V.x 而是  k1 = V.x / V.y
因为所求的切线可能会垂直于 X 轴, 那么这样的斜率就没有意义,同时点斜式写成  x = k1*y + b1
那么 b1 就是与 X 轴的交点, 直接返回即可。
//知道直线方向向量,方向和定点,求与 X 轴的交点,返回交点的横坐标值
double PointOfSkewToPoint(Vector V, Point P) {
double k1 = V.x / V.y; //斜率的倒数 ,方便处理了垂直于 X 轴的情况 x = ky +b;
double b1 = P.x - k1*P.y; // 与 X 轴的交点

return b1;
}


PS:关于点斜式:如果直线有垂直于 X 轴的情况就用  x = k1*y + b1
                        如果直线有垂直于 Y 轴的情况就用  y = k*x + b
                        如果两种情况都可能出现,那么遇到具体问题怎么好处理再具体分析把。

很裸的题目了,也是本弱菜第一个关于圆的几何题目.

注意:

输出的时候线段合并的处理。这里本人贡献一次 WA ,具体代码中标示了,这里不再赘述。感谢 肖煌宇 同学的提醒.

关于 POJ 的 double 型提交问题:贡献一次 OLE (Out Limit Exceed)
POJ : double 的输出如果用 G++ 提交就要用 %f 否则会 OLE
                   如果用 C++ 提交则不会产生这样的问题。
HDU 则完全不会出现上述问题。【提醒来自 kuangbin 大神】

code:

1375Accepted136K32MSC++2705B
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 510;
int n;

struct Point {
double x,y;
Point() {}
Point(double _x, double _y) {
x = _x; y = _y;
}

Point operator - (const Point &b) const {
return Point(x-b.x, y-b.y);
}
double operator *(const Point &b) const {
return x*b.x + y*b.y;
}
double operator ^(const Point &b) const {
return x*b.y - y*b.x;
}

}Ceil;

typedef Point Vector;

//rad是弧度 //向量 A 逆时针旋转 rad Vector Rotate(Vector A, double rad) { return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad)); }
//计算向量极角
double angle(Vector V ) {
return atan2(V.y, V.x);
}
double Length(Vector V) {
return sqrt(V.x*V.x+V.y*V.y);
}

struct Circle {
Point c;
double r;
Circle() {}
Circle(Point _c, double _r) {
c = _c; r = _r;
}
Point point(double a) { // a 为相对于 X 轴的正方向逆时针偏转角度
return Point(c.x+cos(a)*r, c.y+sin(a)*r);
}
}circle;

struct Node{
double st,en;
}ans[maxn];

const double eps = 1e-5;
int dcmp(double x) {
if(x < eps) return 0;
else return x < 0 ? -1 : 1;
}

//知道直线方向向量,方向和定点,求与 X 轴的交点,返回交点的横坐标值 double PointOfSkewToPoint(Vector V, Point P) { double k1 = V.x / V.y; //斜率的倒数 ,方便处理了垂直于 X 轴的情况 x = ky +b; double b1 = P.x - k1*P.y; // 与 X 轴的交点 return b1; }
//处理第 index 个圆, 计算其在 X 轴产生的阴影部分
void Tangents(int index) {
Circle C = circle;

Vector u = C.c - Ceil;
double distance = Length(u);

double ang = asin(C.r / distance);
Vector v1 = Rotate(u, -ang); //st
Vector v2 = Rotate(u, +ang); //en

ans[index].st = PointOfSkewToPoint(v1, Ceil);
ans[index].en = PointOfSkewToPoint(v2, Ceil);
}

bool cmp(Node a, Node b) {
return (a.st == b.st && a.en <= b.en) || a.st < b.st;
}

int main()
{
while(scanf("%d", &n) != EOF) {
if(n == 0) break;
double x,y,r;
scanf("%lf%lf", &x,&y); Ceil = Point(x, y);

for(int i = 0; i < n; i++) { //边输入边处理每一个圆
scanf("%lf%lf%lf", &x, &y, &r);
circle = Circle(Point(x, y), r);
Tangents(i);
}

sort(ans, ans+n, cmp);

int index = 0;
for(int i = 1; i < n; i++) { //合并重叠的
if(ans[i].st <= ans[index].en) ans[index].en = max(ans[index].en, ans[i].en); //注意:是取二者中较大的, 而不是 ans[i].en, 贡献一次WA
else ans[++index] = ans[i];
}

for(int i = 0; i <= index; i++)
printf("%.2lf %.2lf\n", ans[i].st, ans[i].en);
printf("\n");

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息