您的位置:首页 > 其它

Leetcode 149. Max Points on a Line

2017-05-27 16:31 489 查看
题目大意

给定二维坐标下的n个点,求其中共线的点的最大个数。

思路分析

两个不同的点可以确定一条直线,设这两个点的坐标分别为(x1,y1)和(x2,y2),则这条直线的方程式可以写成:(x-x1)(y1-y2)=(y-y1)(x1-x2),化简后可以得到:(y1-y2)x+(x2-x1)y=x2y1-x1y2。设a=y1-y2, b=x2-x1, c=x2y1-x1y2,则a、b、c三个数可以确定一条直线(因两个点不同,a和b不可能同时为0)。

题目中要求共线的点的最大个数,两个点必定共线,那么三个不同的点如何判断共线呢?假设这三个不同的点分别是A(x1,y1), B(x2,y2), C(x3, y3),我们可以先求出A和B确定的直线中a、b、c的值,记为(a1,b1,c1),再计算A和C确定的直线中a、b、c的值,记为(a2,b2,c2)。问题转换成如何根据(a1,b1,c1)和(a2,b2,c2)来确定A、B、C三点是否共线。

我们有以下结论,如果存在一个数g使得,a1*g=a2, b1*g=b2, c1*g=c2成立,则A、B、C三点共线。将(a1,b1,c1)进行如下转换:

如果a1,b1,c1均不为0,寻找三个数的绝对值的最大公约数g1,然后进行如下操作:a1/=g1,b1/=g1,c1/=g1。转第四步。

如果a1,b1,c1中只有一个数为0,则对剩下两个数进行第一步中的操作,并转到第四步。

如果a1,b1,c1中只有一个数不为0,则将该数赋值为1。转第四步。

如果a1<0 或者a1==0&&b1<0,则将三个数取值为其相反数。

经过以上步骤,我们得到一条直线的最简表达式,若要对比两条直线是否相同,只需要对比最简化的(a,b,c)即可。

代码实现

定义表示一条直线的结构体:

typedef struct Line{
long a;
long b;
long c;
Line():a(0),b(0),c(0){}
Line(long x, long y, long z):a(x), b(y), c(z){}
}Line;


求两个数的最小正公约数

int gcd(long a, long b)
{
a = abs(a); b=abs(b);
if(a<b) swap(a,b);
while(b!=0){
long tmp = a%b;
a = b;
b = tmp;
}
return a;
}


求直线的最简表达式

void simplify(Line &line){
if(line.a==0){
if(line.b==0){
if(line.c!=0) line.c=1;
}else{
if(line.c==0) line.b=1;
else{
long g = gcd(line.b, line.c);
line.b/=g;
line.c/=g;
}
}
}else if(line.b==0){
if(line.c==0)line.a=1;
else{
long g = gcd(line.a, line.c);
line.a/=g;
line.c/=g;
}
}else if(line.c==0){
long g = gcd(line.a, line.b);
line.a/=g;
line.b/=g;
}else{
long g = gcd(gcd(line.a, line.b), line.c);
line.a/=g;
line.b/=g;
line.c/=g;
}
if(line.a<0 || (line.a==0 && line.b<0)){
line.a = -line.a;
line.b = -line.b;
line.c = -line.c;
}
}


求最大共线点数目

int maxPoints(vector<Point>& points) {
if(points.size()<3) return points.size();
map<string, set<int>> ma;
int ans = 0;
for(int i=0;i<points.size();i++){
string s = to_string(points[i].x)+"_"+to_string(points[i].y);
ma[s].insert(i);
ans = max(ans, (int)ma[s].size());
for(int j=i+1;j<points.size();j++){
if(points[i].x==points[j].x && points[i].y==points[j].y){
string s = to_string(points[i].x)+"_"+to_string(points[i].y);
if(ma[s].count(j)==0) ma[s].insert(j);
ans = max(ans, (int)ma[s].size());
continue;

4000
}
Line cur((long)points[i].y-points[j].y, (long)points[j].x-points[i].x,
(long)points[j].x*points[i].y-(long)points[i].x*points[j].y);
simplify(cur);
string s = to_string(cur.a)+"_"+to_string(cur.b)+"_"+to_string(cur.c);
if(ma.find(s)==ma.end()){
set<int> se;
se.insert(i);
se.insert(j);
ma[s]=se;
}else{
//cout<<s<<" "<<ma[s].count(i)<<" "<<i<<endl;
if(ma[s].count(i)==0) ma[s].insert(i);
if(ma[s].count(j)==0) ma[s].insert(j);
}
ans = max(ans, (int)ma[s].size());
}
}
return ans;
}


总结

在以上处理过程中,需单独考虑某个点重复出现的情况,这时候应该单独计算这些点的数目(同一个点一定共线)。也可以先将这些点的个数统计出来,记录到临时计数器中,当这些点出现在某条直线时,直接从计数器中取出其个数进行叠加等操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode