CF2C 计算几何

题目大意:

三个不相交的圆,且三个圆心不在同一直线上,圆外找一点,到三个圆的切线角相等(多个,则要求切线角最大),如果没有则不输出

解题思路:

  • 寻思着二分切线角,判断是否三个圆有交集从而往大分往小分,但失败了。。。有个测试点,发现怎样都不对,于是就改了别的方法
  • R i = d i s ( p , o i ) R_i=dis(p,o_i) Ri=dis(p,oi)为圆外点到第 i i i个圆圆心距离,题目要求的切线角相等,其实就是要求 a r c s i n ( r 1 R 1 ) = a r c s i n ( r 2 R 2 ) = a r c s i n ( r 3 R 3 ) arcsin(\frac{r_1}{R_1}) = arcsin(\frac{r_2}{R_2}) = arcsin(\frac{r_3}{R_3}) arcsin(R1r1)=arcsin(R2r2)=arcsin(R3r3)
  • 也就是要求 r 1 R 1 = r 2 R 2 = r 3 R 3 \frac{r_1}{R_1}=\frac{r_2}{R_2}=\frac{r_3}{R_3} R1r1=R2r2=R3r3
  • 那么就是 ( r 1 R 1 − r 2 R 2 ) 2 + ( r 3 R 3 − r 2 R 2 ) 2 + ( r 1 R 1 − r 3 R 3 ) 2 = 0 (\frac{r_1}{R_1} - \frac{r_2}{R_2})^2+(\frac{r_3}{R_3} - \frac{r_2}{R_2})^2+(\frac{r_1}{R_1} - \frac{r_3}{R_3})^2=0 (R1r1R2r2)2+(R3r3R2r2)2+(R1r1R3r3)2=0
  • 对于这类求平面代价最小题目,很多就可以用模拟退火的思想来做
  • 太久没做计算几何的题目了,思维都锈了。。。早该想到的

AC代码

#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 4;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const db eps = 1e-5;
const db pi = acos((db)-1.0);
int sgn(db x) {
    if (fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}
struct Point {
    db x, y, w;
    Point operator - (Point p) {return {x - p.x, y - p.y};}
    Point operator + (Point p) {return {x + p.x, y + p.y};}
    db operator ^ (Point p) {return x * p.y - y * p.x;}
    db dis(Point p) {return hypot(x - p.x, y - p.y);}
} ans;
struct Circle {
    Point p;
    db r;
} c[4];
db energy(Point tmp) {
    db d[4];
    for (int i = 1; i <= 3; i++) d[i] = c[i].p.dis(tmp) / c[i].r;
    db res = 0;
    for (int i = 1; i <= 3; i++) res += (d[i] - d[i % 3 + 1]) * (d[i] - d[i % 3 + 1]);
    return res;
}
int main() {
    for (int i = 1; i <= 3; i++) cin >> c[i].p.x >> c[i].p.y >> c[i].r, ans.x += c[i].p.x, ans.y += c[i].p.y;
    ans.x /= 3.0, ans.y = 3.0;
    ans.w = energy(ans);
    db T = 1;
    while (T > eps) {
        int s = 0;
        ans.w = energy(ans);
        if (energy({ans.x + T, ans.y}) < ans.w) {
            ans.x = ans.x + T;
            s = 1;
        }
        else if (energy({ans.x - T, ans.y}) < ans.w) {
            ans.x = ans.x - T;
            s = 1;
        }
        else if (energy({ans.x, ans.y + T}) < ans.w) {
            ans.y = ans.y + T;
            s = 1;
        }
        else if (energy({ans.x, ans.y - T}) < ans.w) {
            ans.y = ans.y - T;
            s = 1;
        }
        if (!s) T /= 2.0;    //说明可以在当前步长继续走
    }
    ans.w = energy(ans);
    if (ans.w < eps) cout << seteps(5) << ans.x << " " << ans.y << endl;
    return 0;
}
/*
0 0 1
10 10 1
20 0 2
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值