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;
}
posted @ 2023-04-13 00:06  wnsyou  阅读(53)  评论(0编辑  收藏  举报