热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[H枚举]lc149.直线上最多的点数(枚举+细节优化+数学)

文章目录1.题目来源2.题目解析1.题目来源链接:149.直线上最多的点数大佬题解:直线一般式,CSTL应用2.题目解析枚举算法优

文章目录

    • 1. 题目来源
    • 2. 题目解析


1. 题目来源

链接:149. 直线上最多的点数

大佬题解:直线一般式,C++ STL 应用

2. 题目解析

枚举+算法优化。

暴力做法:

  • 枚举直线,两点确定一条直线,O(n2)O(n^2)O(n2)
  • 针对每个直线,确定直线上有多少点,直接套直线方程即可,O(n)O(n)O(n)
  • 总的时间是 O(n3)O(n^3)O(n3)

优化做法:

  • 不先将所有直线枚举出来,而只枚举直线上的一个点,称为中心点
  • 那么经过这个点的直线是 360° 任意多条的。
  • 然后再枚举其他各点。
  • 当枚举另一个点时有两种情况:
    • 它与中心点构成一条新的直线,则保存该直线的斜率即可,用于判断之后的其它点是否在该直线上。
    • 它与中心点构成的直线已经出现过,即斜率相等,则在该直线上加上这个点的数量即可。
  • 任意一点都可能成为中心点,通过枚举其它各点与中心点的连线,我们可以得到所有的直线。每次即可求得当前中心点所在直线的最大点数数量。
  • 先枚举中心点,再枚举其余各点。故这个的复杂度是 O(n2)O(n^2)O(n2) 的。


细节:

  • 针对直线的表示,在此直接使用斜截式表示即可,注意小数精度问题。
  • 斜截式求斜率时,分母为零情况注意,即垂线情况,需要单独用变量纪录这条直线。
  • 如果某点与中心点重叠,则该点会落到所有直线上,需要单独使用变量记录和中心点重叠的点数。
  • 则最终统计哪条直线点数最多时,需要统计比较正常直线上的点数、垂线上的点数,最后再加上和中心点重叠的点数。
  • 本题精度要求较高,需要使用 long double


关于使用两点式和斜截式:

  • 两点式不需要讨论分母为 0 的这种垂线情况,将直线可以直接化为一般式,以下是我一些总结:
    在这里插入图片描述
  • 尤其注意两点式的变形情况,这个方程就不涉及分母为 0 的情况了。简单整理可以得到直线的一般式:Ax+By+C=0等价于x(y2−y1)+y(x1−x2)+x1(y1−y2)+y1(x2−x1)=0Ax+By+C=0 等价于 x(y_2-y_1)+y(x_1-x_2)+x_1(y_1-y_2)+y_1(x_2-x_1)=0Ax+By+C=0x(y2y1)+y(x1x2)+x1(y1y2)+y1(x2x1)=0
    其可以与平面上的直线一一对应,但是不同的 A,B,CA,B,CA,B,C 可能存在倍数关系,即可能出现直线共线的情况。所以需要对 A,B,CA,B,CA,B,C 约掉其最大公约数,保证直线唯一。


极其优质:

  • 直线一般式,C++ STL 应用
  • 实际上只需要一般式中的 A、B 即可表示一个直线

当使用 unordered_map 时,需要自己传入 hash 函数。


时间复杂度:O(n2)O(n^2)O(n2)
空间复杂度:O(n)O(n)O(n)


class Solution {
public:int maxPoints(vector<vector<int>>& points) {typedef long double LD;int res &#61; 0;for (auto p : points) {int s1 &#61; 0, s2 &#61; 0; // s1 和中心点相同&#xff0c;s2 直线unordered_map<LD, int> cnt;for (auto q : points) {if (p &#61;&#61; q) s1 &#43;&#43; ;else if (q[0] &#61;&#61; p[0]) s2 &#43;&#43; ;else {LD k &#61; (LD)(q[1] - p[1]) / (q[0] - p[0]);cnt[k] &#43;&#43; ;}}int c &#61; s2;for (auto [k, v] : cnt) c &#61; max(v, c);res &#61; max(res, c &#43; s1);}return res;}
};


大佬的写法&#xff1a;

arraytuple 均可。类似于这种类型、pair 等均需要自己传入哈希函数。

// 计算三元组的哈希值
struct v3_hash {size_t operator()(const array<int, 3> &x) const {return x[0] ^ x[1] ^ ((size_t)x[2] << 32);}
};class Solution {
public:int maxPoints(vector<vector<int>>& points) {// Ax &#43; By &#43; C &#61; 0, (A, B, C)// A(x1 - x2) &#43; B(y1 - y2) &#61; 0// (A, B, C) &#61; (y2 - y1, x1 - x2, x2 * y1 - x1 * y2)int best &#61; 0;for (int i &#61; 0; i < points.size(); &#43;&#43;i) {// 记录同一条直线的出现次数unordered_map<array<int, 3>, int, v3_hash> cnts;const int x1 &#61; points[i][0];const int y1 &#61; points[i][1];for (int j &#61; i &#43; 1; j < points.size(); &#43;&#43;j) {const int x2 &#61; points[j][0];const int y2 &#61; points[j][1];// 计算表示一条直线的三元组array<int, 3> v3 &#61; {y2 - y1, x1 - x2, x2 * y1 - x1 * y2};bool first_non_zero &#61; true; // 判断是否遇到第一个非零元素bool minus &#61; false; // 记录三元组第一个非零元素是否为负数int g &#61; 0; // 变量g记录最大公约数for (int k &#61; 0; k < 3; &#43;&#43;k) {if (v3[k] !&#61; 0) {if (first_non_zero) {first_non_zero &#61; false;minus &#61; v3[k] < 0;g &#61; abs(v3[k]);} else {g &#61; __gcd(g, abs(v3[k]));}}}// 对三元组进行约分if (g !&#61; 0) {if (minus) g &#61; -g;for (int k &#61; 0; k < 3; &#43;&#43;k) {if (v3[k] !&#61; 0) v3[k] /&#61; g;}}best &#61; max(best, &#43;&#43;cnts[v3]);}}// 直线上最多的点数 &#61; 同一条直线出现的最大次数 &#43; 1return best &#43; 1;}
};

简单优化&#xff0c;在此题目保证了各点互不相同&#xff0c;则 gcd() 一定不为 0&#xff0c;不会出现除 0 错误&#xff0c;则对 A、B、C 求最大公约数除了就行了&#xff0c;但是会不会差一个符号呢&#xff1f;

其实可能是会的&#xff0c;__gcd 是忽略符号的&#xff0c;即 __gcd(-2, -6)&#61;-2&#xff0c;则正号依旧是正号&#xff0c;负号依旧是负号&#xff0c;若 &#43; - &#43; &#61; 0- &#43; - &#61; 0&#xff0c;那么 gcd 后两者符号刚好交换&#xff0c;等价于两条直线了&#xff0c;但在本题貌似没有出现这个情况。


不知道能否严格证明该情况不会出现。 或是这次对了只是侥幸&#xff1f;

struct v3_hash {size_t operator()(const array<int, 3> &x) const {return x[0] ^ x[1] ^ ((size_t)x[2] << 32);}
};class Solution {
public:int maxPoints(vector<vector<int>>& points) {int best &#61; 0;for (int i &#61; 0; i < points.size(); &#43;&#43;i) {unordered_map<array<int, 3>, int, v3_hash> cnts;const int x1 &#61; points[i][0];const int y1 &#61; points[i][1];for (int j &#61; i &#43; 1; j < points.size(); &#43;&#43;j) {const int x2 &#61; points[j][0];const int y2 &#61; points[j][1];// 计算表示一条直线的三元组int A &#61; y2 - y1, B &#61; x1 - x2, C &#61; x2 * y1 - x1 * y2;int t &#61; __gcd(__gcd(A, B), __gcd(B, C));A /&#61; t, B /&#61; t, C /&#61; t; // 直接化简array<int, 3> v3 &#61; {A, B, C};best &#61; max(best, &#43;&#43;cnts[v3]);}}return best &#43; 1;}
};


推荐阅读
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
author-avatar
Amyb__ing舒
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有