Leetcode 149. Max Points on a Line

题目大意

给定二维坐标下的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使得,a1g=a2, b1g=b2, c1*g=c2成立,则A、B、C三点共线。将(a1,b1,c1)进行如下转换:

  1. 如果a1,b1,c1均不为0,寻找三个数的绝对值的最大公约数g1,然后进行如下操作:a1/=g1,b1/=g1,c1/=g1。转第四步。
  2. 如果a1,b1,c1中只有一个数为0,则对剩下两个数进行第一步中的操作,并转到第四步。
  3. 如果a1,b1,c1中只有一个数不为0,则将该数赋值为1。转第四步。
  4. 如果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;
                }
                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;
    }

总结

在以上处理过程中,需单独考虑某个点重复出现的情况,这时候应该单独计算这些点的数目(同一个点一定共线)。也可以先将这些点的个数统计出来,记录到临时计数器中,当这些点出现在某条直线时,直接从计数器中取出其个数进行叠加等操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值