【題解】A566.三點共線

Macw發表於2024-04-25

題目大意,給定在平面直角座標系中的多個點,判斷有多少個三元組 \((A, B, C)\) 滿足共線性質。

題目連結:A566.三點共線

大題思路就是暴力所有的三元組,判斷三個元素的斜率是否相同即可。其實還有其他方法可以做,我個人感覺用斜率法最簡單。

有幾點需要注意:

  1. 在計算斜率的時候,如果多個點處於一個與橫座標軸垂直的線上,那麼除以 \(0\) 的時候會爆\(\color{royalblue}\text{RE}\) 需要特判一下。

  2. 儲存的時候需要使用 double 型別。

  3. 在選取三元組的時候,需要保證不重複不遺漏。不會出現一個點被多次選中,相同的組合被多次計算的情況。

  4. 斜率法

    對於三個點 \((x_1, y_1)\), \((x_2, y_2)\), 和 \((x_3, y_3)\),計算任意兩點之間的斜率。如果這三個斜率相等,則這三個點共線。但是要注意的是,當兩個點的 x 座標相等時,斜率會無窮大,因此在實際計算中需要特別處理這種情況。

    \[\frac{dy}{dx} = \frac{{y_2 - y_1}}{{x_2 - x_1}} \]

#include <iostream>
using namespace std;

struct point{
    int x;
    int y;
} arr[105]; 
int n, cnt = 0;

int main(){
	cin >> n;
    for (int i=1; i<=n; i++)
        cin >> arr[i].x >> arr[i].y;
    
    for (int i=1; i<=n; i++){
        for (int j=i+1; j<=n; j++){
            for (int k=j+1; k<=n; k++){
                int x1 = arr[i].x; int x2 = arr[j].x; int x3 = arr[k].x;
                int y1 = arr[i].y; int y2 = arr[j].y; int y3 = arr[k].y;
				if (x1 - x2 == 0 && x3 - x2 == 0){
                    cnt++;
                    continue;
                }
                if (x1 - x2 == 0 || x3 - x2 == 0) 
                    continue;
                double s1 = 1.0 * (y2 - y1) / (x2 - x1);
                double s2 = 1.0 * (y3 - y2) / (x3 - x2);
                if (s1 == s2) cnt++;
            }
        }
    }
    cout << cnt << endl;
    return 0;
}
  1. 向量法

    設想將三個點看作向量,即 \(\vec{P_1P_2}\)\(\vec{P_1P_3}\)。如果這兩個向量是平行的,則三個點共線。你可以透過計算這兩個向量的叉積來驗證它們是否平行。如果叉積為零,則兩個向量平行,即三個點共線。

    \[\text{Cross Product} = \vec{P_1P_2} \times \vec{P_1P_3} = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1) \]

#include <iostream>
using namespace std;

struct point{
    int x;
    int y;
} arr[105]; 
int n, cnt;

int main(){
	cin >> n;
    for (int i=1; i<=n; i++)
        cin >> arr[i].x >> arr[i].y;
    
    for (int i=1; i<=n; i++){
        for (int j=i+1; j<=n; j++){
            for (int k=j+1; k<=n; k++){
                int x1 = arr[i].x; int x2 = arr[j].x; int x3 = arr[k].x; 
                int y1 = arr[i].y; int y2 = arr[j].y; int y3 = arr[k].y;
				if ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1) == 0) 
					cnt++;
            }
        }
    }
    cout << cnt << endl;
    return 0;
}
  1. 行列式法

    將三個點的座標表示成矩陣形式,然後計算這個矩陣的行列式。如果行列式的值為零,則表示這三個點共線。有關行列式的計算可以自行在搜尋引擎上搜尋。

    \[\text{Determinant} = \begin{vmatrix} x_1 & y_1 & 1 \\ x_2 & y_2 & 1 \\ x_3 & y_3 & 1 \end{vmatrix} = 0 \]

#include <iostream>
using namespace std;

struct point{
    int x;
    int y;
} arr[105]; 
int n, cnt;

int main(){
	cin >> n;
    for (int i=1; i<=n; i++)
        cin >> arr[i].x >> arr[i].y;
    
    for (int i=1; i<=n; i++){
        for (int j=i+1; j<=n; j++){
            for (int k=j+1; k<=n; k++){
                double x1 = arr[i].x; double x2 = arr[j].x; double x3 = arr[k].x; 
                double y1 = arr[i].y; double y2 = arr[j].y; double y3 = arr[k].y;
				if (x1 * y2 + y1 * x3 + x2 * y3 - x1 * y3 - y2 * x3 - x2 * y1 == 0)
					cnt++;
            }
        }
    }
    cout << cnt << endl;
    return 0;
}
  1. 面積法

    如果三個點 \(A(x_1, y_1)\), \(B(x_2, y_2)\), 和 \(C(x_3, y_3)\) 共線,則它們構成的三角形的面積為零。

    \[S_{area} = \frac{1}{2} |x_1(y_2 - y_3) + x_2(y_3 - y_1) + x_3(y_1 - y_2)| \]

#include <iostream>
using namespace std;

struct point{
    int x;
    int y;
} arr[105]; 
int n, cnt;

int main(){
	cin >> n;
    for (int i=1; i<=n; i++)
        cin >> arr[i].x >> arr[i].y;
    
    for (int i=1; i<=n; i++){
        for (int j=i+1; j<=n; j++){
            for (int k=j+1; k<=n; k++){
                int x1 = arr[i].x; int x2 = arr[j].x; int x3 = arr[k].x; 
                int y1 = arr[i].y; int y2 = arr[j].y; int y3 = arr[k].y;
                if (0.5 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3*(y1-y2)) == 0)
					cnt++;
            }
        }
    }
    cout << cnt << endl;
    return 0;
}

以上所有程式碼的時間複雜度為 \(O(n^3)\),其中 \(n\) 是點的數量。但對於本題而言,沒有問題不會超時。

相關文章