abc248_e K-colinear Line 题解
K-colinear Line
题意
平面直角坐标系上给出 \(n\) 个点,第 \(i\) 个点的坐标为 \((x_i, y_i)\)。
请求出平面上有多少条直线穿过 \(n\) 个点中的至少 \(k\) 个点。如果有无数条这样的直线,输出 Infinity
。
数据范围
- \(1 \leqslant K \leqslant N \leqslant 300\)
- \(|X_i|, |Y_i| \leqslant 10^9\)
- 当 \(i \ne j\) 时,\(X_i \ne X_j\) 或者 \(Y_i \ne Y_j\),即点的坐标两两不同。
思路
首先来一个特判,当 \(k = 1\) 时,答案肯定无穷大,即输出 Infinity
。
然后,观察到数据范围那么小,考虑最暴力的做法:枚举两个点,两点确定一条直线,求出有多少个点在这条直线上,统计答案即可,为了去重,我们可以用一个标记数组,每次求出一条直线时都将其上的点两两标记为求过了即可。
为了判断某个点是否在直线上,需要亿点点的数学知识,公式如下:
- 确定了一条直线,穿过了点 \(i\) 和点 \(j\),如果点 \(k\) 在这条直线上,必然满足下面这个条件
- \(\frac{x_j-x_i}{y_j-y_i} = \frac{x_k-x_i}{y_k-y_i}\),为了防止精度误差,使用交叉相乘,得到第二个式子:\((x_j - x_i) \times (y_k - y_i) = (y_j - y_i) \times (x_k - x_i)\)
模拟即可,注意爆int
的细节。
复杂度
- 时间:\(O(n^3)\)
- 空间:\(O(n^2)\)
Code
点击查看代码
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
int k, n, f[310][310];
ll x[310], y[310], ans;
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
if (k == 1) { // 无穷大
cout << "Infinity";
} else {
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (!f[i][j]) { // 去重
vector<int> v; // 记录这条线上有哪些点
v.push_back(i), v.push_back(j);
for (int l = j + 1; l <= n; l++) {
if (1ll * (x[j] - x[i]) * (y[l] - y[i]) == 1ll * (x[l] - x[i]) * (y[j] - y[i])) { // 套用数学公式
v.push_back(l);
}
}
if (v.size() >= k) { // 满足要求
ans++;
}
for (int l : v) { // 注意了啊!这里是一个伪 n^4,实际上这两重循环总时间复杂度为 n^2。
for (int m : v) {
f[l][m] = 1; // 标记
}
}
}
}
}
cout << ans;
}
return 0;
}