【NEUQ2075】New Game!(*)(幾何+dijkstra)
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; }