Jack Straws POJ - 1127 (計算兩直線的交點)
阿新 • • 發佈:2018-10-31
題意:桌子上放著n根木棍,木棍的兩端座標分別是(Pix, Piy)和(Qix, Qiy)。給定m對木棍(ai, bi),請判斷沒對木棍是否相連。當兩根木棍之間有公共點時,就認為它們是相連的。通過相連的木棍間接的連在一起的兩根木棍也認為是相連的。
題解:
分析:
木棍就是二維平面上的線段,只有能夠判斷線段是否相交,那麼建圖以後就可以輕鬆的進行連線性判斷。那麼,應該如何判斷兩條線段是否相交呢?首先會想到計算兩直線的交點,然後判斷交點是否線上段上這一方法。那麼兩條直線的交點要怎麼求得呢?雖然可以把直線表示成方程,通過聯立方程組求解,但是在幾何問題中,運用向量的內積和外積進行計算是非常方便的。對於二維向量p1=(x1, y1) 和 p2=(x2, y2),我們定義內積p1*p2 = x1x2 +y1y2,外積p1*p2 = x1y2 – x2y1。要判斷點q是否線上段p1-p2上,只有先利用外積根據是否有(p1-q)*(p2 – q)=0來判斷點q是否在直線p1-p2上,在利用內積根據是否有(p1-q)*(p2– q)<=0來判斷點q是否落在p1-p2之間。而要求兩直線的交點,通過變數t將直線p1-p2上的點表示為p1+t(p2-p1),交點又在直線q1-q2上,所以有:
(q2-q1)*(p1+t(p2-p1)-q1) = 0
於是可以利用下式求得t的值
P1+(q2-q1)*(q1-p1)*(p2-p1)/(q2-q1)*(p2-p1)
但是,使用這個方法時還有注意邊界情況。讓我們來看看樣例中的木棍2和木棍4,這兩條線段是平行的,對應直線沒有交點。但平行的線段也可能有公共點,所有此時需要特別注意。對此有不同的處理方法,這裡我們選擇通過檢查端點是否在另一條線段上來判斷。
附上程式碼:
#include <cstdio> #include <cstring> #include <cmath> using namespace std; double EPS = 1e-10; //考慮誤差的加法運算 double add(double a, double b) { if (abs(a + b) < EPS * (abs(a) + abs(b))) return 0; return a + b; } //二維向量結構體 struct P { double x, y; P(){} P(double x, double y) : x(x), y(y){} P operator + (P p){ return P(add(x, p.x), add(y, p.y)); } P operator - (P p){ return P(add(x, -p.x), add(y, -p.y)); } P operator * (double d){ return P(x * d, y * d); } double dot(P p){ //內積 return add(x * p.x, y * p.y); } double det(P p){ //外積 return add(x * p.y, -y * p.x); } }; //判斷dianq是否線上段p1-p2上 bool on_seg(P p1, P p2, P q) { return (p1 - q).det(p2 - q) == 0 && (p1 - q).dot(p2 - q) <= 0; } //計算直線p1-p2與直線q1-q2的交點 P intersection(P p1, P p2, P q1, P q2) { return p1 + (p2 - p1) * ((q2 - q1).det(q1 - p1) / (q2 - q1).det(p2 - p1)); } //輸入 int n; P p[15], q[15]; int m; int a, b; bool g[15][15]; //相連關係圖 void solve() { memset(g, false, sizeof(g)); for (int i = 0; i < n; i++){ g[i][i] = true; for (int j = 0; j < i; j++){ //判斷木棍i和木棍j是否有公共點 if ((p[i] - q[i]).det(p[j] - q[j]) == 0){ //平行時 g[i][j] = g[j][i] = on_seg(p[i], q[i], p[j]) || on_seg(p[i], q[i], q[j]) || on_seg(p[j], q[j], p[i]) || on_seg(p[j], q[j], q[i]); } else{ //非平行時 P r = intersection(p[i], q[i], p[j], q[j]); g[i][j] = g[j][i] = on_seg(p[i], q[i], r) && on_seg(p[j], q[j], r); } } } //通過Floyd-Warshall演算法判斷任意兩點間是否相連 for (int k = 0; k < n; k++){ for (int i = 0; i < n; i++){ for (int j = 0; j < n; j++){ g[i][j] |= g[i][k] && g[k][j]; } } } while (scanf("%d%d", &a, &b) != EOF){ if (a == 0 && b == 0){ break; } puts(g[a - 1][b - 1] ? "CONNECTED" : "NOT CONNECTED"); } } int main() { while (scanf("%d", &n), n){ for (int i = 0; i < n; i++){ scanf("%lf%lf%lf%lf", &p[i].x, &p[i].y, &q[i].x, &q[i].y); } solve(); } return 0; }