今天下午讲了平面计算几何的大部分内容,知识密度比较大,在这里做简单总结。方法比较偏数学,也相对直观,理解起来比较简单,但是相对的代码量不小,想熟练掌握还要练题。
平面计算几何课堂笔记
一般来讲大型比赛(区域赛、区域赛决赛)中必出,难度是中等到高等难度,代码量较大,且要考虑精度(太高太低都不行,小概率会莫名被卡)和特判问题,所以选手一般放在后面写。
用一个类(class)or结构体来表示一个矢量(用坐标的形式),重载操作符来或者是写个函数来表示操作。
关于操作符(operator)重载可以参考:https://blog.csdn.net/liitdar/article/details/80654324
基本的矢量操作均非常容易。(对称操作可以通过类似镜像阵的构造向量操作来实现)
二维中叉乘:p.x*q.y-q.x*p.x(最常用)
叉乘直接与方向、面积相关,因此与之相关的所有问题都可以用叉乘作为基础来解决。
有向面积(平行四边形,有正负,在二维中,看旋转向即可,这样就给出了一个1.顺序)
POJ 2007 凸多边形点的顺序
凸多边形面积:三角剖分+海伦公式(麻烦)
2.多边形面积(叉乘):找一个点(任一点)按序算各个两个点的组与之构成的有向三角形面积,算一圈累加即可。
3.点线(矢量)关系:判断点在直线的哪一侧(计算几何中直线(因为是矢量)有方向)
两条直线的交点->两个线段的交点(规范相交,另外还有9种情况,带板子,准备充分)
补一下,我还不太会
精度不够和精度太高导致WA:例如判断两个点重合(重叠点计数,这样结果是整数,因此误差会被放大)。
能不牵扯角度就不牵扯角度,因为会拖慢速度且会降低精度(增大误差)。
与浮点数相关的运算不能写==(a-b>or<FPS)
double PI=acos(-1);
double INF=1e20;
double EPS=1e-6;
点绕点旋转/点到直线垂线(足)/…
题
POJ 1569
判断点是否在三角形(多边形)内部:(就是拓扑可定向问题,类似斯托克斯公式的定向)
先把边排序给出(使之连接后是一个封闭曲线),如果点在每一条(相邻两个顶点连成的)直线的同侧,则点在多边形的内部。(出现不同侧则在外部)
进一步地,如何判断这个序是顺时针还是逆时针?(对于凹凸多边形均可用)
方法一:考察最左下角的点和其前点与后点的关系O(nlogn)(需要找点)
方法二:遍历叉乘符号(点乘符号?):看如果不是凸多边形统计符号差(顺时针负号多,逆时针正号多)O(n);凸多边形(顺时针只有负号,逆时针只有正号)O(1).(此法应该有拓扑依据,但是现在想不起来)
旋转卡壳法(钉子和木板)
解释:
因为直线的普通情况不可穷尽,无法枚举,因此枚举极值情况。
一般地,直线的可行解在可行域内可以任意地平移与旋转,因此,旋转到卡壳就是其边界,也就是极值-因此,往往要枚举题目中约束线段端点。
POJ 3304
问题:给定一些线段,问是否存在直线L,使得所有线段到L的所有投影至少有一个公共点。
解题思路:如果有存在这样的直线,过投影相交区域作直线的垂线,该垂线必定与每条线段相交,问题转化为问是否存在一条线和所有线段相交。
而被某两个端点限定的两个直线就是可行域中的边界。所以枚举这些直线是否与所有线段都相交即可。
POJ 1039
枚举由上部分一个顶点与下部分一个顶点所限定的直线,其中要判定与折线管道不相交
判定:关于不相交,可以枚举每一部奋的端点(如果上部的所有端点都在一侧;下部的端点都在另一侧则可以说明光线与管道不相交)
POJ 1066 没啥意思 连直线
进阶
点在多边形内外:
1.叉乘-拓扑定向
2. O(n)射线法(最常用):(射线与边界的)交点奇数点在内,交点偶数点在外,在边界上要特判。(具体怎么实现?)
因为有特殊情况,所以要找比较别扭(随机)的射线(防hack);实在不行就两条射线,保证交点个数一致。
3. 旋角法
凸包(最常用,考查最频繁)
(不严格地说)对欧式空间来说,离散点集最小的凸边界(顶点由属于集合的点组成,且刚好包裹整个点集)
(意义:保证任何两点的连线都在凸多边形内部与边界)
凸包唯一吗?
轨迹唯一,但是要说明可能有属于点集的点作为凸包边上的点出现。
O(N^2)卷包裹法(点是钉子,拿橡皮筋一绕)
最左最低(最左下)(其必然是凸包的一个端点)为起点,枚举起点与所有点的连线,优先与水平线夹角最小的,以其作为新起点迭代。
O(NlogN)Graham扫描法
最左最低(最左下)(其必然是凸包的一个端点)为起点
极角序
(注意极角序保留不了全部的凸包上的非端点,会由于排序规则的不同而舍弃漏掉的部分)
按极角从小到大排序,同角近在前(or远在前)
用stack维护:先加入p0和p1;
(注:左拐or右拐or直走是预备点的后点相对于预备点来说的。)
遍历所有点之后结束。
左拐加入作为预备点,右拐弹出预备点,直走看情况
(保证凸和小)
(如果想保留全部边界则不弹出,不想则弹出)
水平序(代码相对简单但是有特殊情况不适用)
大致框架与极角序相似。
排序先x后y(or先y后x)
对所有的点,类似的stack模拟过程,出现一半壳。
对所有的点逆序同上操作,出现另一半壳,拼起来就是整个凸包。
补一下,我还不太会
总结:不要求保留凸包上的非端点则用极角序,(代码量少,复杂度相同)要求则用水平序(事实上对于极角序实际上可以加个补丁:可以用如线段树的lazy-tag的操作,对于同角不同距离的点,延迟弹出,即到全部弹出时才弹出。)。
POJ 1113 Wall
凸包扩大一下即得,总周长=凸包周长加一个圆的周长(半径就是最小距离)(易证明)。
POJ 1228恢复农场
做凸包,如果任意一条边上有大于等于3个钉子(点),则可确定。
半平面交(线性规划?)
(半平面=有向直线+(一般左)侧)
有限半平面交必然为凸区域
无穷大的区域上的问题可以用一个足够大的矩形框进去。
POJ 1474
能看到的点<=>(转360度)的直线与边界的交点能扫描整个边界。
<=>以边界线段为边界的半平面的交生成的区域内的任意点
晚上训练赛:
打得比较压抑,出了一道签到题之后一直卡在题意上(完全看不懂在说什么...按自己理解的题意与样例试了好几发都wa了,后来补题的时候才搞明白题的意思,题本身很简单,但是一直读不懂题一直卡...
还是要仔细读题,尽量完全读懂再去做。