题意
给出 n 条线段端点的坐标,然后给出若干组询问。每组询问包含两个数字,输出这两个数字代表的线段是否联通。线段从 1 到 n 编号。通过联通的线段间接连在一起的线段,认为这两条线段也是联通的。
思路
判断两线段是否相交,首先求出两线段所在直线的交点,然后看这个交点是否在两条线段上。如果在,则两条线段相交。
可以利用坐标求两直线的方程然后进行运算,但运用向量的内积和外积更简单。
内积与外积
内积 = x1 * y1 + x2 * y2 , 外积 = x1 * y 2 - x2 * y1 。
外积的公式可以借助行列式来记忆,对于三维坐标也适用。
判断点是否在线段上
1.利用外积判断点q是否在p1 - p2线段所在的直线上
线段所在直线的方向向量可以表示成 p1 - p2 。
点在直线上,则有 (p1 - q) X (p2 - q) = 0 。
2.利用内积进一步判断点是否在线段上
点在直线上的基础上,进一步判断点是否落在线段内,即线段的两端点之间。
直线上的点在线段上,则有 (p1 - q) * (p2 - q) <= 0 。
利用外积求两直线(p1 - p2, q1 - q2)的交点
设一个变量 t,则直线 p1 - p2 上的点可以表示成 p1 + t (p1 - p2) 。又该点也在直线 q1 - q2 上,所有有 (q2 - q1) X (p1 + t(p1 - p2) - p1) = 0 。解出 t 的表达式后代入 p1 + t(p1 - p2) 即得到了交点的向量即坐标:
p1 + (p2 - p1) * ((q2 - q1).det(q1 - p1) / (q2 - q1).det(p2 - p1));
特殊情况
如果两条线段所在直线的方向向量平行,这两条线段依然有可能相交。对于这种情况,若只需要判断线段的端点即可。
若两条线段中,至少一条线段的至少一个端点在另一条线段上,则这两条线段相交。
算法过程
1.首先判断两条线段所在直线的方向向量是否平行,若平行,则按照特殊情况处理
2.对于不平行的情况,首先利用公式求出两线段所在直线的交点,然后判断这个交点是否在两条线段上。如果在,则两条线段相交。
3.最后,利用floyd算法求出线段联通关系的图即可。
注意
1.几何问题一定要注意特殊情况的处理
具体分析可见《挑战程序设计竞赛 253 页》
2.计算误差问题
1.设置 eps,一般为 1e-10 。
a < 0 == a < -eps
a <= 0 == a < eps
a = 0 == abs(a) < eps
2.注意平方运算会使误差快速变大
3.考虑误差