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\n",dist[2]);
}
}