题意:求两个圆的公切线。
题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1615
——>>发现一个精度问题:对于点与点的大小比较:
以前我这样写:(可以A一些题目)
bool operator < (const Point& e) const{
return x < e.x || (dcmp(x - e.x) == 0 && y < e.y);
}
可是,这样写在此题中会WA。。。
改成:
bool operator < (const Point& e) const{
return dcmp(x - e.x) < 0 || (dcmp(x - e.x) == 0 && dcmp(y - e.y) < 0);
}
这样就可以AC啦。。。感觉以后碰浮点数比较,都用上dcmp的较为稳妥。。。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const double eps = 1e-10;
const double PI = acos(-1);
int dcmp(double x){
return fabs(x) < eps ? 0 : (x > 0 ? 1 : -1);
}
struct Point{
double x;
double y;
Point(double x = 0, double y = 0):x(x), y(y){}
bool operator < (const Point& e) const{
return dcmp(x - e.x) < 0 || (dcmp(x - e.x) == 0 && dcmp(y - e.y) < 0);
}
int read(){
return scanf("%lf%lf", &x, &y);
}
}p[3];
typedef Point Vector;
Vector operator + (Point A, Point B){
return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Point A, Point B){
return Vector(A.x - B.x, A.y - B.y);
}
Vector operator * (Point A, double p){
return Vector(A.x * p, A.y * p);
}
Vector operator / (Point A, double p){
return Vector(A.x / p, A.y / p);
}
double Dot(Vector A, Vector B){
return A.x * B.x + A.y * B.y;
}
double Cross(Vector A, Vector B){
return A.x * B.y - B.x * A.y;
}
double Length(Vector A){
return sqrt(Dot(A, A));
}
struct Circle{
double x, y;
double r;
Circle(double x = 0, double y = 0, double r = 0):x(x), y(y), r(r){}
int read(){
return scanf("%lf%lf%lf", &x, &y, &r);
}
Point point(double a){
return Point(x + r * cos(a), y + r * sin(a));
}
};
int getTangents(Circle A, Circle B, Point *a, Point *b){
int cnt = 0; //存切点用
if(dcmp(A.r - B.r) < 0){
swap(A, B);
swap(a, b);
}
double d = sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y)); //圆心距
double rdiff = A.r - B.r; //两圆半径差
double rsum = A.r + B.r; //两圆半径和
if(dcmp(d - rdiff) < 0) return 0; //1.内含
double base = atan2(B.y - A.y, B.x - A.x); //向量AB的极角
if(dcmp(d) == 0) return -1; //2.重合
if(dcmp(d - rdiff) == 0){ //3.内切
a[cnt] = b[cnt] = A.point(base);
cnt++;
return 1;
}
double ang = acos((A.r - B.r) / d);
a[cnt] = A.point(base + ang); b[cnt] = B.point(base + ang); cnt++; //4.相交(外切、外离的外公切线也在此求出)
a[cnt] = A.point(base - ang); b[cnt] = B.point(base - ang); cnt++; //两条外公切线的切点
if(dcmp(d - rsum) == 0){ //5.外切
a[cnt] = b[cnt] = A.point(base);
cnt++;
}
else if(dcmp(d - rsum) > 0){ //6.外离
double ang = acos((A.r + B.r) / d);
a[cnt] = A.point(base + ang); b[cnt] = B.point(PI + base + ang); cnt++;
a[cnt] = A.point(base - ang); b[cnt] = B.point(PI + base - ang); cnt++;
}
return cnt;
}
int main()
{
Circle A, B;
Point a[5], b[5];
while(A.read() == 3 && B.read() == 3){
if(!A.x && !A.y && !A.r && !B.x && !B.y && !B.r) return 0;
int cnt = getTangents(A, B, a, b);
printf("%d\n", cnt);
if(cnt == -1 || cnt == 0) continue;
vector<pair<Point, Point> > tan;
for(int i = 0; i < cnt; i++) tan.push_back(make_pair(a[i], b[i]));
sort(tan.begin(), tan.end());
for(vector<pair<Point, Point> >::iterator iter = tan.begin(); iter != tan.end(); iter++)
printf("%.5f %.5f %.5f %.5f %.5f\n", iter->first.x, iter->first.y, iter->second.x, iter->second.y, Length(iter->second - iter->first));
}
return 0;
}