題目:http://acm.hdu.edu.cn/showproblem.php?pid=4773
題意:給定兩個圓,告訴半徑和圓心,它們是相離的,在這兩個圓外給定一個點p,求符合條件:過點p的圓且與已知的兩個
圓外切的所有圓的總數和它們的圓心坐標和半徑。
分析:根據題意,我們設已知兩個圓的半徑分別為和,它們的圓心分別為和,設點p的坐標為
并設要求的圓的圓心為,半徑為,那么根據它們的關系我們可以很快就列出方程組:
然后,現在的問題就是來解,但是你會發現這個方程是很難解的,因此為了簡化問題,我們利用反演變換來做。
那么,怎么用反演變換來做? 首先,得知道什么是反演變換以及它有什么性質。
反演的定義:
已知一圓C,圓心為O,半徑為r,如果P與P’在過圓心O的直線上,且,則稱P與P'關于O互為反演。
反演的性質:
(1)除反演中心外,平面上的每一個點都只有唯一的反演點,且這種關系是對稱的,位于反演圓上的點,保持在原處,位于
反演圓外部的點,變為圓內部的點,位于反演圓內部的點,變為圓外部的點。 舉個最簡單的例子,區間以1為反演
半徑,那么反演后的區間就是,這就是一維反演,而圓的反演是二維反演。
(2)任意一條不過反演中心的直線,它的反形是經過反演中心的圓,反之亦然,特別地,過反演中心相交的圓,變為不過反
演中心的相交直線。
定理:不過反演中心的圓,它的反形是一個圓,反演中心是這兩個互為反形的圓的一個位似中心,任意一對反演點是逆對應
點。用圖形來解釋,如下圖:
那么,對于一個不過反演中心的圓,怎樣求它的反形圓?
很容易知道我們只需要求出反形圓的圓心和半徑就可以了。
對于上圖我們設圓C1的半徑為,C2的半徑為,反演半徑為
那么根據反演的定義有:
那么,消去得到:
這樣我們就得到了反形圓的半徑,那么還要求反形圓的圓心。
由于C1和O兩點的坐標已知,而且我們知道O,C1,C2位于同一直線上,那么很明顯對于C2的坐標,我們可以這樣計算:
設O的坐標為,C1的坐標為,C2的坐標為
那么有:
至于由上面解處可以很容易得到,這樣我們就完成了圓的反演變換。
由于本題的做法是這樣的,先以點P為為反演中心,反演半徑隨便設置都可以,為了計算方便就設為1,把圓C1和圓C2反演后再求這兩個圓的
公切線,再把這個公切線反演回去,那么就是一個過點P的圓,且與原來的C1和C2相切。
那么接下來就是如何計算兩個圓的公切線了。這里只需要考慮公切線在同一側的情況。那么,這個自己畫圖就能很容易計算了。
找到公切線后還要把它反演成圓,這個圓還經過P點,那么很容易得到了。
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <math.h>using namespace std;
double const eps = 1e-8;struct Point
{double x,y;Point(double a = 1.0,double b = 1.0):x(a),y(b){}Point operator + (const Point &a){return Point(x+a.x,y+a.y);}Point operator - (const Point &a){return Point(x-a.x,y-a.y);}Point operator * (const double a){return Point(a*x,a*y);}Point Trans(){return Point(-y,x);}void Input(){scanf("%lf%lf",&x,&y);}
} ;struct Circle
{Point o;double r;Circle(Point a = Point(),double b = 1.0):o(a),r(b) {}Point getPoint(double alpha){return o + Point(r*cos(alpha),r*sin(alpha));}void Input(){o.Input();scanf("%lf",&r);}void Output(){printf("%.8lf %.8lf %.8lf\n",o.x,o.y,r);}
} ;Point p;
Circle c[15];double dist(Point A,Point B)
{return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y));
}double cross(Point A,Point B,Point C)
{return (B.x-A.x)*(C.y-A.y) - (B.y-A.y)*(C.x-A.x);
}int sign(double x)
{return (x > eps) - (x < -eps);
}Circle Inverse(Circle C)
{Circle T;double t = dist(C.o,p);double x = 1.0 / (t - C.r);double y = 1.0 / (t + C.r);T.r = (x - y) / 2.0;double s = (x + y) / 2.0;T.o = p + (C.o - p) * (s / t);return T;
}void add(Point a,Point b,int &k)
{double t = cross(a,p,b);if(t < 0) t = -t;double d = dist(a,b);t /= d;if(t > eps){double w = 0.5 / t;Point dir = (b-a).Trans();Point a1 = p + dir * (w / d);Point b1 = p - dir * (w / d);if(fabs(cross(a,b,a1)) < fabs(cross(a,b,b1)))c[k++] = Circle(a1,w);elsec[k++] = Circle(b1,w);}
}int Work()
{c[0] = Inverse(c[0]);c[1] = Inverse(c[1]);if(c[1].r > c[0].r) swap(c[1],c[0]);Point v = c[1].o - c[0].o;double alpha = atan2(v.y,v.x);double d = dist(c[0].o,c[1].o);double beta = acos((c[0].r - c[1].r) / d);int k = 2;Point a = c[0].getPoint(alpha + beta);Point b = c[1].getPoint(alpha + beta);if(sign(cross(a,c[0].o,b)) == sign(cross(a,p,b)) &&sign(cross(a,c[1].o,b)) == sign(cross(a,p,b)))add(a,b,k);a = c[0].getPoint(alpha - beta);b = c[1].getPoint(alpha - beta);if(sign(cross(a,c[0].o,b)) == sign(cross(a,p,b)) &&sign(cross(a,c[1].o,b)) == sign(cross(a,p,b)))add(a,b,k);return k - 2;
}int main()
{int T;scanf("%d",&T);while(T--){c[0].Input();c[1].Input();p.Input();int num = Work();printf("%d\n",num);for(int i=0;i<num;i++)c[i+2].Output();}return 0;
}
總結
以上是生活随笔為你收集整理的圆的反演变换的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。