Medium
Minimum Number of Lines to Cover Points — C++
Full explanation · Time O(n * 2^n) · Space O(n^2)
// Time: O(n^2 + n*2^n) = O(n*2^n)
// Space: O(n^2)
// math, hash table, bitmasks
class Solution {
private:
struct TupleHash {
template <typename... T>
std::size_t operator()(const std::tuple<T...>& t) const {
return apply([](const auto&... args) {
std::size_t seed = 0;
((seed ^= std::hash<std::decay_t<decltype(args)>>{}(args) +
0x9e3779b9 + (seed << 6) + (seed >> 2)), ...);
return seed;
}, t);
}
};
public:
int minimumLines(vector<vector<int>>& points) {
auto ceil_divide = [](int a, int b) {
return (a + b - 1) / b;
};
unordered_map<tuple<int, int, int>, unordered_set<tuple<int, int>, TupleHash>, TupleHash> lookup;
for (int i = 0; i < size(points); ++i) {
const int x1 = points[i][0], y1 = points[i][1];
for (int j = i + 1; j < size(points); ++j) {
const int x2 = points[j][0], y2 = points[j][1];
// (x-x1)/(x2-x1) = (y-y1)/(y2-y1)
// => (y2-y1)x - (x2-x1)y = x1(y2-y1) - y1(x2-x1)
const int dx = x2 - x1, dy = y2 - y1;
const auto& g = gcd(dx, dy);
int a = dx / g, b = dy / g;
if (a < 0 || (a == 0 && b < 0)) {
a = -a, b = -b;
}
const int c = b * x1 - a * y1;
lookup[{a, b, c}].emplace(x1, y1);
lookup[{a, b, c}].emplace(x2, y2);
}
}
vector<tuple<int, int, int>> lines;
for (const auto& [l, p] : lookup) {
if (size(p) > 2) { // filter to improve complexity
lines.emplace_back(l);
}
}
assert(size(lines) <= size(points) / 2); // 1 extra colinear point per 2 points
int result = numeric_limits<int>::max();
const int mask_upper_bound = 1 << size(lines);
for (int mask = 0; mask < mask_upper_bound; ++mask) {
unordered_set<tuple<int, int>, TupleHash> covered;
for (int i = 0, bit = 1; bit <= mask; bit <<= 1, ++i) {
if (mask & bit) {
for (const auto& x : lookup[lines[i]]) {
covered.emplace(x);
}
}
}
result = min(result, __builtin_popcount(mask) + ceil_divide(size(points) - size(covered), 2));
}
return result;
}
};