題目大意,給定在平面直角座標系中的多個點,判斷有多少個三元組 \((A, B, C)\) 滿足共線性質。
題目連結:A566.三點共線。
大題思路就是暴力所有的三元組,判斷三個元素的斜率是否相同即可。其實還有其他方法可以做,我個人感覺用斜率法最簡單。
有幾點需要注意:
-
在計算斜率的時候,如果多個點處於一個與橫座標軸垂直的線上,那麼除以 \(0\) 的時候會爆\(\color{royalblue}\text{RE}\) 需要特判一下。
-
儲存的時候需要使用
double
型別。 -
在選取三元組的時候,需要保證不重複不遺漏。不會出現一個點被多次選中,相同的組合被多次計算的情況。
-
斜率法:
對於三個點 \((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;
}
-
向量法:
設想將三個點看作向量,即 \(\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;
}
-
行列式法:
將三個點的座標表示成矩陣形式,然後計算這個矩陣的行列式。如果行列式的值為零,則表示這三個點共線。有關行列式的計算可以自行在搜尋引擎上搜尋。
\[\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;
}
-
面積法:
如果三個點 \(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\) 是點的數量。但對於本題而言,沒有問題不會超時。