-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
minimum-number-of-lines-to-cover-points.cpp
73 lines (70 loc) · 2.78 KB
/
minimum-number-of-lines-to-cover-points.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Time: O(n^2 + n*2^n) = O(n*2^n)
// Space: O(n^2)
// math, hash table, bitmasks
class Solution {
private:
template <typename A, typename B, typename C>
struct TupleHash {
size_t operator()(const tuple<A, B, C>& p) const {
size_t seed = 0;
A a; B b; C c;
tie(a, b, c) = p;
seed ^= std::hash<A>{}(a) + 0x9e3779b9 + (seed<<6) + (seed>>2);
seed ^= std::hash<B>{}(b) + 0x9e3779b9 + (seed<<6) + (seed>>2);
seed ^= std::hash<C>{}(c) + 0x9e3779b9 + (seed<<6) + (seed>>2);
return seed;
}
};
template <typename T>
struct PairHash {
size_t operator()(const pair<T, T>& p) const {
size_t seed = 0;
seed ^= std::hash<T>{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2);
seed ^= std::hash<T>{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2);
return seed;
}
};
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<pair<int, int>, PairHash<int>>, TupleHash<int, int, int>> 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)
int a = y2 - y1;
int b = -(x2 - x1);
int c = x1 * (y2 - y1) - y1 * (x2 - x1);
const int g = gcd(gcd(a, b), c);
a /= g, b /= g, c /= g;
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<pair<int, int>, PairHash<int>> 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;
}
};