CCPC-Wannafly Camp #2 (部分题解)

L: New Game!

题目描述:

Eagle Jump公司正在开发一款新的游戏。泷本一二三作为其员工,获得了提前试玩的机会。现在她正在试图通过一个迷宫。

这个迷宫有一些特点。为了方便描述,我们对这个迷宫建立平面直角坐标系。迷宫中有两条平行直线 L\_1:Ax+By+C\_1=0L1​:Ax+By+C1​=0, L\_2:Ax+By+C\_2=0L2​:Ax+By+C2​=0,还有 nn 个圆 C\_i:(x-x\_i)^2+(y-y\_i)^2={r\_i}^2Ci​:(x−xi​)2+(y−yi​)2=ri​2。角色在直线上、圆上、圆内行走不消耗体力。在其他位置上由SS点走到TT点消耗的体力为SS和TT的欧几里得距离。

泷本一二三想从 L\_1L1​ 出发,走到 L\_2L2​ 。请计算最少需要多少体力。

输入:

第一行五个正整数 n,A,B,C\_1,C\_2n,A,B,C1​,C2​ (1\le n \le 1000, -10000 \le A,B,C\_1,C\_2 \le 10000)(1≤n≤1000,−10000≤A,B,C1​,C2​≤10000),其中 A,BA,B 不同时为 0。

接下来 nn 行每行三个整数 x,y,r(-10000 \le x,y \le 10000, 1\le r \le 10000)x,y,r(−10000≤x,y≤10000,1≤r≤10000) 表示一个圆心为 (x,y)(x,y),半径为 rr 的圆。

输出:

仅一行一个实数表示答案。与标准答案的绝对误差或者相对误差不超过 10^{-4}10−4 即算正确。

样例输入

2 0 1 0 -4
0 1 1
1 3 1

样例输出

0.236068

L1 到 L2 之间连边权值 |C1−C2|  / √ A2+B2

线 L 与圆 i 之间连边权值 max(0, d(Oi , L1) − ri)

圆 i 与圆 j 之间连边权值 max(0, d(Oi , Oj ) − ri − rj )

求 L1 到 L2 的最短路即可。

#include<bits/stdc++.h>
#define ios1 ios::sync_with_stdio(0)
#define ios2 cin.tie(0)
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 5000;
int n, A, B, C1, C2;
double Map[maxn][maxn];
 
struct node{
    double x, y, r;
}circle[maxn];
 
void init() {
    for(int i = 0 ; i <= n + 1; i++){
        for(int j = 0; j <= n + 1; j++) {
            if(i == j)Map[i][j] = 0;
            else Map[i][j] = inf;
        }
    }
}
 
bool cmp (node p, node q) {
    if(p.x == q.x)return p.y < p.y;
    return p.x < p.y;
}
 
 
double dis[maxn];
bool vis[maxn];
 
 
void Dijkstra() {
    for (int i = 0; i <= n+1; i++) { 
        dis[i] = Map[0][i];
    }
    vis[0] = 1;
    for (int i = 0; i < n+1; i++) {
        double MIN = inf; 
        int x = -1;
        for (int j = 0; j <= n+1; j++) {
            if (!vis[j] && dis[j]<MIN) { 
                MIN = dis[j];
                x = j;
            }
        }
        vis[x] = 1;
        for (int j = 0; j <= n+1; j++) {
            if (!vis[j] && MIN + Map[x][j]<dis[j]) {
                dis[j] = Map[x][j] + MIN;
            }
        }
    }
}
 
 
int main() {
    ios1; ios2;
    while(cin >> n >> A >> B >> C1 >> C2) {
        for(int i = 1; i <= n; i++) {
            cin >> circle[i].x >> circle[i].y >> circle[i].r;
        }
        init();
        Map[0][n+1] = Map[n+1][0] = abs(C1-C2)/sqrt(A*A+B*B);
        for(int i = 1; i <= n; i++) {
            double k = abs(A*circle[i].x + B*circle[i].y + C1)/sqrt(A*A+B*B) - circle[i].r;
            if(k < 0)k = 0;
            Map[0][i] = Map[i][0] = k;
        }
        for(int i = 1; i <= n; i++) {
            double k = abs(A*circle[i].x + B*circle[i].y + C2)/sqrt(A*A+B*B) - circle[i].r;
            if(k < 0)k = 0;
            Map[i][n+1] = Map[n+1][i] = k;
        }
        for(int i = 1 ; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                if(i != j){
                    double k = sqrt(abs(circle[i].x - circle[j].x) * abs(circle[i].x - circle[j].x) + abs(circle[i].y - circle[j].y) * abs(circle[i].y - circle[j].y)) - circle[i].r - circle[j].r;
                    if(k < 0)k = 0;
                    Map[i][j] = k;
                }
            }
        }
        memset(vis, 0, sizeof(vis));
        Dijkstra();
        printf("%lf\n", dis[n+1]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值