開啟章節選單
10355 Superman
題目說明
有一個超人,他要在一個空間座標中從A點飛到B點,但在空間座標中有n個球形的汙染區域 要求讀取測資到EOF 對於每一個測資,第一行會輸入超人的名字 第二行給6個數字,前三個代表起點座標,後三個代表終點座標 第三行有一個數字n代表汙染區域的個數 接下來n行代表n個汙染區域,每一行都包含四個數字,前三個代表汙染區域的中心,第四個代表半徑。
解題過程
球體在空間座標的函數為(x-a)^2+(y-b)^2+(z-c)^2=r^2 直線可以用動點(bx+kvx,by+kvy,bz+kvz)表達 其中bx,by,bz代表起點座標,vx,vy,vz代表直線的向量(以終點-起點距離計算),k則是一個變數,我們要算出當動點停在球面上時k的值,再反推回動點座標,進而求出直線中,在球體內的長度。 將動點代入球體函數展開時,會得到一個一元二次方程式,透過公式解得到k1及k2(分別對應兩個直線與球的交會點) 最後再考慮到起點或終點在汙染區內的可能( k<0 or k>1) 用這個計算公式計算每一個球,並加總直線在球內的長度,算出總和長度/路徑長100,再處理浮點數即可 輸出要求針對每一個側資都輸出兩行,第一行輸出超人名字,第二行輸出要通過汙染區的距離跟總距離的比例
考點:高中數學:空間座標與方程式
程式碼
#include <iostream> #include <cmath> using namespace std; double dis(double bx, double by, double bz, double ex, double ey, double ez, double a, double b, double c, double r) { double vx = ex - bx, vy = ey - by, vz = ez - bz; double A = vx*vx + vy*vy + vz*vz; double B = 2 * ((bx - a) * vx + (by - b) * vy + (bz - c) * vz); double C = (a - bx) * (a - bx) + (b - by) * (b - by) + (c - bz) * (c - bz) - r * r; if (B * B - 4 * A * C <= 0) return 0;//superman don't go in poison area double n = sqrt(B * B - 4 * A * C); double k1 = (-B + n) / 2 / A, k2 = (-B - n) / 2 / A; k1 = max(0.0, k1); k2 = max(0.0, k2); k1 = min(1.0, k1); k2 = min(1.0, k2);//superman start or end in poison area double kq = k1 - k2; return sqrt((kq * vx) * (kq * vx) + (kq * vy) * (kq * vy) + (kq * vz) * (kq * vz)); } int main() { string s; while(cin >> s) { cout << s << endl; double x, y, z, a, b, c; cin >> x >> y >> z >> a >> b >> c; double k, total=0; cin >> k; for(;k ;k--) { double p1, p2, p3, r; cin >> p1 >> p2 >> p3 >> r; total += dis(x, y, z, a, b, c, p1, p2, p3, r); } printf("%.2f\n",total / sqrt((x - a)*(x - a)+(y - b)*(y - b)+(z - c)*(z - c))*100); } }