POJ 1375 Intervals【過定點做圓的切線】

鏈接:



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




算法: 計算幾何  定點做圓的切線 向量的旋轉



題意:


天花板上有一個點, 下面有 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:

1375 Accepted 136K 32MS C++ 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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章