LeetCode - 解题笔记 - 149 - Max Points on a Line

Solution 1

【参考官方】

这道题太经典了,本科的时候就练习过,然而我还是忘了。

整体思路就是穷举法,不是很方便优化。穷举思路就是比照每两点之间的直线方程,但是考虑到所有的线都有基准点,那么只要比较斜率就可以了(平行线情形在有基准点的情况下不会影响结果)。

难点就是斜率的表示。【没错这里我还是不会,我是垃圾】

首先,给定两点 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) ( x 2 , y 2 ) (x_2, y_2) (x2,y2),其斜率即 x 1 − x 2 y 1 − y 2 \frac{x_1 - x_2}{y_1 - y_2} y1y2x1x2,由于浮点数的保存形式,该结果不能保存成,只能用分数表示。

其次,不同的分数表示需要约分,因此需要使用最大公约数统一到质数形式。

再有,垂直线和水平线有一项为0,这个时候同一将另外一项处理成1。

最后,正负号问题,同一处理成y项为正。

此外还参考官方实现增加了几个剪枝:

  1. 当输入只有少于两个点时,直接输出。
  2. 只检查当前点顺序之后的点,因为其之前的点在这之前已经遍历完成。
  3. 在上述实现基础上,如果当前的最优结果已经小于待考察点个数或者超过一半,不用继续。
  • 时间复杂度: O ( n 2 ⋅ log ⁡ m ) O(n^2 \cdot \log m) O(n2logm),其中 n n n为点的个数, m m m为横坐标最大值,前者二次项为枚举方案,对数项为GCD计算负载。
  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n为点的个数,哈希表的最大开销。
class Solution {
public:
    struct hash_pair{
        size_t operator()(const pair<int, int> & p) const{
            return p.second + p.first * 20001; // dy + dx * 20001
        }
    };
    
    int maxPoints(vector<vector<int>>& points) {
        int n = points.size();
        if (n <= 2) {
            return n;
        }
        
        int ans = 1;
        for (int i = 0; i < n; i++) {
            if (ans >= n - i || ans > n / 2) {
                break;
            }
            unordered_map<pair<int, int>, int, hash_pair> lines;
            
            for (int j = i + 1; j < n; j++) {
                // 只考虑后面的点
                int deltaX = points[i][0] - points[j][0];
                int deltaY = points[i][1] - points[j][1];
                
                if (deltaX == 0) {
                    deltaY = 1; // 水平线
                } else if (deltaY == 0) {
                    deltaX = 1; // 垂直线
                } else {
                    if (deltaY < 0) {
                        // 统一化正负号
                        deltaX = -deltaX;
                        deltaY = -deltaY;
                    }
                    // 最约化
                    int m = gcd(abs(deltaX), abs(deltaY));
                    deltaX /= m;
                    deltaY /= m;
                }
                
                
                lines[{deltaX, deltaY}]++;
            }
            
            int maxP = 0;
            for (auto it = lines.begin(); it != lines.end(); ++it) {
                maxP = max(maxP, it->second + 1);
            }
            ans = max(ans, maxP);
        }
        
        return ans;
    }
    
private:
    int gcd(int a, int b) {
        return b ? gcd(b, a % b) : a;
    }
};

Solution 2

Solution 1的Python实现

class Solution:
    def maxPoints(self, points: List[List[int]]) -> int:
        n = len(points)
        if n <= 2: return n
        
        ans = 1
        for i in range(n):
            if ans >= n - 1 or ans > n // 2: break
                
            lines = collections.defaultdict(int)
            
            for j in range(i + 1, n):
                deltaX = points[i][0] - points[j][0]
                deltaY = points[i][1] - points[j][1]
                
                if deltaX == 0: deltaY = 1
                elif deltaY == 0: deltaX = 1
                else:
                    if deltaY < 0:
                        deltaX = -deltaX
                        deltaY = -deltaY
                        
                    m = math.gcd(abs(deltaX), abs(deltaY))
                    deltaX /= m
                    deltaY /= m
                    
                lines[(deltaX, deltaY)] += 1
                
            maxP = 0
            for _, value in lines.items():
                maxP = max(maxP, value + 1)
                
            ans = max(ans, maxP)
            
        return ans
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值