1. 问题描述:
约翰的农场可以看作一个二维平面。农场中有 n 个老鼠,在毁坏着农田。第 i 个老鼠的位置坐标为 (xi,yi)。不同老鼠可能位于同一位置。在 (x0,y0) 处,装有一个双向发射的激光枪,该位置没有老鼠。激光枪每次发射都可以将穿过点 (x0,y0) 的某一条直线上的所有老鼠都消灭掉。请问,为了消灭所有老鼠,至少需要激光枪发射几次。
输入格式
第一行包含三个整数 n,x0,y0,表示共有 n 只老鼠,激光枪的位置为 (x0,y0)。接下来 n 行,每行包含两个整数 xi,yi,表示第 i 只老鼠的位置为 (xi,yi)。
输出格式
一个整数,表示激光枪的最少发射次数。
数据范围
前 5 个测试点满足 1 ≤ n ≤ 5。
所有测试点满足 1 ≤ n ≤ 1000,−10 ^ 4 ≤ xi,yi ≤ 10 ^ 4。
输入样例1:
4 0 0
1 1
2 2
2 0
-1 -1
输出样例1:
2
输入样例2:
2 1 2
1 1
1 0
输出样例2:
1
来源:https://www.acwing.com/problem/content/4312/
2. 思路分析:
分析题目可以知道实际上我们需要求解的是所有点与源点(x0,y0)构成的直线的数目,所以我们需要根据点与点的坐标计算出直线的斜率这样两个点就可以确定一条直线,所以问题就转化为了如何对直线进行判重?也即判断他们是否是重合的,一般有两种方法:① 计算斜率k和截距b,k和b相同那么所有点都在这条直线上;② 存储一个向量(x - x0, y - y0)用来标识一条直线;由于第一种方法计算斜率k的时候有可能会产生精度问题,而第二种方法存储的其实是一个向量那么是没有精度问题的,一般采取第二种方法来存储,这样可以标识一条直线,并且这个向量应该是最简的,也即x - x0和 y - y0的最大公约数为1,这样就可以唯一标识直线的方向,由于向量是有方向的,所以我们固定x - x0是正的这样相对方向就确定了,使用哈希表记录所有标识直线的向量即可,最终哈希表的大小就是直线的条数。
3. 代码如下:
class Solution:
# 求解a, b的最大公约数
def gcd(self, a: int, b: int):
return a if b == 0 else self.gcd(b, a % b)
def process(self):
n, x0, y0 = map(int, input().split())
mp = dict()
for i in range(n):
x, y = map(int, input().split())
x -= x0
y -= y0
# 求解x和y的最大公约数最终x, y才是最简的
g = self.gcd(x, y)
# (x, y)表示一个向量
x //= g
y //= g
if x < 0:
# 将第一个数字变为正数这样相对方向就固定了, 例如(1, 1)和(-1, -1)标识的其实是一条直线
x = -x
y = -y
mp[(x, y)] = 1
return len(mp)
if __name__ == '__main__':
print(Solution().process())