题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6097
题意:
给一个圆C和圆心O,P、Q是圆上或圆内到圆心距离相等的两个点,在圆上取一点D,求|PD| + |QD|的最小值
反演点定义:https://baike.baidu.com/item/反演点/5735267?fr=aladdin
做点P、Q的反演点P'、Q', |OP| * |OP'| = r ^ 2, |OQ| * |OQ'| = r ^ 2.
|OP| / r = r / |OP'|, 即|OP| / |OD| = |OD| / |OP'|, 所以△OPD ∽ △ODP', 同理, △OQD ∽ △ODQ', 相似比为|OP| / r.
所以, |PD| + |QD| = (|P'D| + |Q'D|) * |OP| / r. 转化为求|P'D| + |Q'D|的最小值
若P'Q'与圆C有交点,则|P'D| + |Q'D|的最小值为|P'Q'| (此时D为P'Q'与圆C的交点, |P'D| + |Q'D| 为直线,值最小)
若P'Q'与圆C无交点,则D为PQ的中垂线与圆C的交点时, |P'D| + |Q'D|取得最小值(以P、Q为焦点做椭圆,令椭圆不断变大,椭圆与圆C有一个交点时,此交点即为点D)
代码:
# include <iostream>
# include <algorithm>
# include <cstdio>
# include <cstring>
# include <cmath>
# include <cstdlib>
using namespace std;
typedef long long ll;
const double eps = 1e-7;
double xp, yp, xq, yq;
double r;
int main(void)
{
int T; scanf("%d", &T);
while (T-- && scanf("%lf", &r)) {
scanf("%lf %lf %lf %lf", &xp, &yp, &xq, &yq);
double op = sqrt(xp * xp + yp * yp);
if (fabs(op) < eps) { printf("%.7lf\n", 2 * r); continue; }
double l = r * r / op;
double xp2 = xp * l / op, yp2 = yp * l / op;
double xq2 = xq * l / op, yq2 = yq * l / op;
double xm = (xp2 + xq2) / 2, ym = (yp2 + yq2) / 2;
if (xm * xm + ym * ym <= r * r) {
double ans = sqrt((xq2 - xp2) * (xq2 - xp2) + (yq2 - yp2) * (yq2 - yp2)) * op / r;
printf("%.7f\n", ans);
} else {
double d = sqrt(xm * xm + ym * ym);
double t = r / d;
double x = xm * t, y = ym * t;
double ans = 2 * sqrt((x - xp) * (x - xp) + (y - yp) * (y - yp));
printf("%.7f\n", ans);
}
}
return 0;
}
/*
5
4
4 0
0 4
4
0 3
3 0
4
0 2
2 0
4
0 1
1 0
4
0 0
0 0
*/