2075: 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=ri2。角色在直线上、圆上、圆内行走不消耗体力。在其他位置上由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
【解题思路】
题目中说在直线、圆上以及圆内运动时不消耗体力,所以这里可以将整条直线看做一个起始点0,第二条直线看做一个终点n+1,想要得到最优解的最好办法就是从起点出发一直能在圆上或圆内运动直到达到终点,那么就可以根据每个圆与两条直线的距离、每两个圆之间的距离建图(即圆心到直线的距离-r,圆心到圆心的距离-r1-r2),跑一遍dijkstra,即是最少体力。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
const int INF=0x3f3f3f3f;
double edge[maxn][maxn],dis[maxn];
int vis[maxn];
struct Node
{
int x,y,r;
}node[maxn];
int A,B,C1,C2,n;
double dottoline(int A,int B,int C,int x,int y)
{
int t1=A*x+B*y+C;
double t2=sqrt(A*A+B*B);
return fabs(t1*1.0/t2);
}
double dottodot(int x1,int y1,int x2,int y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void dijkstra()
{
memset(vis,0,sizeof(vis));
for(int i=0;i<=n+1;i++)
dis[i]=INF;
dis[0]=0;
while(1)
{
int v=-1;
for(int i=0;i<=n+1;i++)
{
if(!vis[i] && (v==-1 || dis[i]<dis[v]))
v=i;
}
if(v==-1)break;
vis[v]=1;
for(int i=0;i<=n+1;i++)
dis[i]=min(dis[i],dis[v]+edge[v][i]);
}
}
int main()
{
scanf("%d%d%d%d%d",&n,&A,&B,&C1,&C2);
for(int i=0;i<maxn;i++)
for(int j=0;j<maxn;j++)
edge[i][j]=INF;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&node[i].x,&node[i].y,&node[i].r);
double s1=edge[i][0]=dottoline(A,B,C1,node[i].x,node[i].y)-node[i].r;
if(s1<0)edge[0][i]=edge[i][0]=0;
else edge[0][i]=edge[i][0]=s1;
double s2=dottoline(A,B,C2,node[i].x,node[i].y)-node[i].r;
if(s2<0)edge[n+1][i]=edge[i][n+1]=0;
else edge[n+1][i]=edge[i][n+1]=s2;
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
double s=dottodot(node[i].x,node[i].y,node[j].x,node[j].y)-node[i].r-node[j].r;
if(s<0)edge[i][j]=edge[j][i]=0;
else edge[i][j]=edge[j][i]=s;
}
}
dijkstra();
printf("%.6f\n",dis[n+1]);
return 0;
}