Max Points on a Line
Problem:
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
分析:
初读题目,想到的思路是求每两个点之间的直线,然后判断剩下的点有多少在这条直线上。一共有n(n-1)/2条直线,而判断剩余的点一共有n-1个,所以这样的算法复杂度是O(n^3)。为了降低算法的时间复杂度,改进的点主要是用map存储下来已经做过判断的直线,这样可以快速判断新添加的点落在哪个直线上。题目中的点均是整数点,直线的表示方法最好选一般式,即ax + by + c = 0,这样能够保证a, b, c均是整数,在map中作为键值的时候可以自定义偏序关系。
1 struct Coef { 2 int a; 3 int b; 4 int c; 5 6 Coef() : a(0), b(0), c(0) {} 7 Coef(int x, int y, int z) : a(x), b(y), c(z) {} 8 9 friend bool operator<(const Coef &x, const Coef &y) 10 { 11 if(x.a != y.a) { 12 return x.a < y.a; 13 } else if(x.b != y.b) { 14 return x.b < y.b; 15 } else if(x.c != y.c) { 16 return x.c < y.c; 17 } else { 18 return false; 19 } 20 } 21 };
对于即将要判断的两个点(x1, y1)与(x2, y2),他们的一般式为(y2-y1)x - (x2-x1)y +x2y1-x1y2 = 0(这里姑且取b=x2-x1,前面的符号没有影响)。但还要注意最简形式的约分,要求三个数的最大公约数。例如点(0, 0)、(1, 1)求得 a = 1, b = 1, c = 0,而点(0, 0)、(2, 2)求得a = 2, b = 2, c = 0,这三个点共线,但两个直线方程系数不同是因为后面一组不是最简形式。除此之外有负号的情况也要考虑,比如点(0, 0)、(-1, -1)与上面的两组都共线,但因为有负号也会不同,所以全部应该化为最简形式。
void simpleForm() { int cd = gcd(gcd(a, b), c); if(cd > 0) { a /= cd; b /= cd; c /= cd; } if(a < 0) { a *= -1; b *= -1; c *= -1; } else if(a == 0 && b < 0) { b *= -1; c *= -1; } else if(a == 0 && b == 0 && c < 0) { c *= -1; } }
最后需要注意的就是测试的边界条件,比如集合中只有一个点,还有集合中的点有重复。这些边界条件都容易造成WA。完整的代码可以参考下面。
1 int gcd(int a, int b) 2 { 3 if(a < 0) 4 a *= -1; 5 if(b < 0) 6 b *= -1; 7 8 if(a < b) { 9 swap(a, b); 10 } 11 12 while(b != 0) { 13 int r = a % b; 14 a = b; 15 b = r; 16 } 17 return a; 18 } 19 20 struct Coef { 21 int a; 22 int b; 23 int c; 24 25 Coef() : a(0), b(0), c(0) {} 26 Coef(int x, int y, int z) : a(x), b(y), c(z) {} 27 28 void simpleForm() 29 { 30 int cd = gcd(gcd(a, b), c); 31 if(cd > 0) { 32 a /= cd; 33 b /= cd; 34 c /= cd; 35 } 36 37 if(a < 0) { 38 a *= -1; 39 b *= -1; 40 c *= -1; 41 } else if(a == 0 && b < 0) { 42 b *= -1; 43 c *= -1; 44 } else if(a == 0 && b == 0 && c < 0) { 45 c *= -1; 46 } 47 } 48 49 friend bool operator<(const Coef &x, const Coef &y) 50 { 51 if(x.a != y.a) { 52 return x.a < y.a; 53 } else if(x.b != y.b) { 54 return x.b < y.b; 55 } else if(x.c != y.c) { 56 return x.c < y.c; 57 } else { 58 return false; 59 } 60 } 61 }; 62 63 class Solution { 64 public: 65 int maxPoints(vector<Point> &points) { 66 if(points.size() <= 2) 67 return points.size(); 68 69 int maxPoint = 0; 70 for(int i = 0; i < points.size(); ++i) { 71 int sameCounts = 0; 72 map<Coef, int> counts; 73 for(int j = i+1; j < points.size(); ++j) { 74 Point p1 = points[i]; 75 Point p2 = points[j]; 76 77 if(p1.x == p2.x && p1.y == p2.y) { 78 ++sameCounts; 79 continue; 80 } 81 82 int a = p2.y - p1.y; 83 int b = p2.x - p1.x; 84 int c = p2.x * p1.y - p1.x * p2.y; 85 86 Coef coef(a, b, c); 87 coef.simpleForm(); 88 89 if(counts.find(coef) == counts.end()) { 90 counts[coef] = 1; 91 } else { 92 ++counts[coef]; 93 } 94 } 95 96 int curMax = 0; 97 map<Coef, int>::iterator it = counts.begin(); 98 for(; it != counts.end(); ++it) { 99 if(it->second > curMax) 100 curMax = it->second; 101 } 102 103 if(sameCounts + curMax + 1> maxPoint) 104 maxPoint = sameCounts + curMax + 1; 105 } 106 107 return maxPoint; 108 } 109 };