UPC-9519 New Game(最短路)

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

这个迷宫有一些特点。为了方便描述,我们对这个迷宫建立平面直角坐标系。迷宫中有两条平行直线 L1:Ax+By+C1=0,L2:Ax+By+C2=0,还有 n 个圆 。角色在直线上、圆上、圆内行走不消耗体力。在其他位置上由S点走到T点消耗的体力为S和T的欧几里得距离。

泷本一二三想从L1出发,走到L2。请计算最少需要多少体力。

输入
第一行五个正整数n,A,B,C1,C2(1≤n≤1000,−10000≤A,B,C1,C2≤10000),其中A,B 不同时为 0。
接下来 n 行每行三个整数x,y,r(−10000≤x,y≤10000,1≤r≤10000) 表示一个圆心为 (x,y),半径为 r 的圆。

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

样例输入
2 0 1 0 -4
0 1 1
1 3 1

样例输出
0.236068

题解: 这题很像之前一道最短路的题,关于宇宙射线的,具体可以看https://blog.csdn.net/kuronekonano/article/details/80518997

在这道题中,把收到最短时间宇宙射线换成了最小精力花费。
实际上可以把起点和终点两条平行直线看做一个点,每个圆也看做一个点,然后建边,边权即距离,所有圆到起点和终点都有一条边,边权为圆心到直线的距离减去半径,圆与圆之间的边权(相离)圆心两点之间距离减去两圆半径,最后注意起点和终点存在一条边,边权为两平行直线间的距离。

然后要说明一点是,之前一直以为题意是可以在园内走,其实是不行的,只能在圆的边上上走,也就是说,在圆之间移动时,相交和相离是可以直接用上述方法计算的,相交切直接边权为0,但是内含就会出现问题:
在这里插入图片描述
要用两圆半径之差的绝对值减去两圆心距离得到要走到路径。

最后建完图直接跑一个最短路即可。
注意起点和终点注意判断圆是否与直线相交相切。相交相切边权为0

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
double dist[maxn],mp[maxn][maxn];
bool vis[maxn];
int n;
void SPFA(int s)
{
    queue<int>q;
    while(!q.empty())q.pop();
    for(int i=0; i<=n; i++)dist[i]=1e14;
    memset(vis,false,sizeof vis);
    dist[s]=0;
    vis[s]=true;
    q.push(s);
    while(!q.empty())
    {
        int top=q.front();
        q.pop();
        vis[top]=false;
        for(int i=1; i<=n; i++)
        {
            if(dist[i]>dist[top]+mp[top][i])
            {
                dist[i]=dist[top]+mp[top][i];
                if(!vis[i])
                {
                    vis[i]=true;
                    q.push(i);
                }
            }
        }
    }
}
double cal(double x1,double y1,double x2,double y2)
{
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double line(double a,double b,double c,double x,double y)
{
    return fabs(a*x+b*y+c)/sqrt(a*a+b*b);
}
double x[maxn],y[maxn],r[maxn];
int main()
{
    double a,b,c1,c2;
    while(scanf("%d%lf%lf%lf%lf",&n,&a,&b,&c1,&c2)!=EOF)
    {
        n+=2;
        r[1]=r[2]=0;
        mp[2][1]=mp[1][2]=fabs(c1-c2)/sqrt(a*a+b*b);
        for(int i=3; i<=n; i++)
        {
            scanf("%lf%lf%lf",&x[i],&y[i],&r[i]);
            mp[1][i]=mp[i][1]=line(a,b,c1,x[i],y[i]);
            if(r[i]>=mp[1][i])mp[1][i]=mp[i][1]=0;
            else mp[1][i]=mp[i][1]=mp[1][i]-r[i];
            mp[i][2]=mp[2][i]=line(a,b,c2,x[i],y[i]);
            if(r[i]>=mp[2][i])mp[2][i]=mp[i][2]=0;
            else mp[2][i]=mp[i][2]=mp[2][i]-r[i];
            for(int j=3; j<i; j++)
            {
                double tmp=cal(x[i],y[i],x[j],y[j]);
                if(r[i]+r[j]>=tmp)
                {
                    if(fabs(r[i]-r[j])>tmp) mp[i][j]=mp[j][i]=fabs(r[i]-r[j])-tmp;
                    else mp[j][i]=mp[i][j]=0;
                }
                else mp[i][j]=mp[j][i]=tmp-r[i]-r[j];
            }
        }
        SPFA(1);
        printf("%f
",dist[2]);
    }
}

原文地址:https://www.cnblogs.com/kuronekonano/p/11135684.html