判断两直线是否相交

P(x1,y1) Q(x2,y2) 两向量的叉积为 x1*y2-x2*y1

如果 $p\times q$>0 p在q的顺时针方向(右手螺旋定则)

$p \times q$<0 p在q的逆时针方向

=0 ,共线或反向

先做一次快速排斥实验,判断下一个线段中 x 较大的端点是否小于另一个线段中 x 较小的段点,若是,则说明两个线段必然没有交点,同理判断下 y

代码

max(C.x,D.x)<min(A.x,B.x) || max(C.y,D.y)<min(A.y,B.y) ||
max(A.x,B.x)<min(C.x,D.x) || max(A.y,B.y)<min(C.y,C.y)

如图所示,如果想判断两线段相交,只需要判断A 和 B在cd两侧即可

所以只需要 向量AD*CDBD*CD异号即可

如果端点正好在另一条线段上,两者乘积等于0

如果两者平行,叉积也为0但是可以在快速排斥实验中排除掉

总代码

struct Line {
double x1;
double y1;
double x2;
double y2;
};

bool intersection(const Line &l1, const Line &l2)
{
//快速排斥实验
if ((l1.x1 > l1.x2 ? l1.x1 : l1.x2) < (l2.x1 < l2.x2 ? l2.x1 : l2.x2) ||
(l1.y1 > l1.y2 ? l1.y1 : l1.y2) < (l2.y1 < l2.y2 ? l2.y1 : l2.y2) ||
(l2.x1 > l2.x2 ? l2.x1 : l2.x2) < (l1.x1 < l1.x2 ? l1.x1 : l1.x2) ||
(l2.y1 > l2.y2 ? l2.y1 : l2.y2) < (l1.y1 < l1.y2 ? l1.y1 : l1.y2))
{
return false;
}
//跨立实验
if ((((l1.x1 - l2.x1)*(l2.y2 - l2.y1) - (l1.y1 - l2.y1)*(l2.x2 - l2.x1))*
((l1.x2 - l2.x1)*(l2.y2 - l2.y1) - (l1.y2 - l2.y1)*(l2.x2 - l2.x1))) > 0 ||
(((l2.x1 - l1.x1)*(l1.y2 - l1.y1) - (l2.y1 - l1.y1)*(l1.x2 - l1.x1))*
((l2.x2 - l1.x1)*(l1.y2 - l1.y1) - (l2.y2 - l1.y1)*(l1.x2 - l1.x1))) > 0)
{
return false;
}
return true;
}

参考

判断点是否在多边形内部

我们先将横纵坐标存在一个数组内

第一步

if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
// 这个测试都过不了。。。直接返回false;
}

这个测试是画一个四边形

第二步, 这里我们就要讲一个定理了,以某一点为端点画一条射线,如果穿过图形次数为奇数次,则在图形内,如果是偶数次,在图形外

为了方便讨论,我们将以x轴正方向做一条射线

int pnpoly (int nvert, float *vertx, float *verty, float testx, float testy) {
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ( (verty[i]>testy) != (verty[j]>testy) ) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}

nvert 是 顶点数量,testx和testy是顶点横纵坐标

第一段的意思是

verty[i] <testy < verty[j]

或者

verty[j] <testy < verty[i]

这段代码是用来粗略判断射线是否会经过该边的(没考虑反向和端点)

第二段是用来判断测试点是否在两点连线之下,这里用了斜率(移下项)

c=!c;是用来判断奇数次还是偶数次的

多边形的面积

s=pow(p(p-a)(p-b)*(p-c),0.5),p=(a+b+c)/2

凸多边形都可以通过划分变成三角形

凸包

用最少的点把给出的点全部包住

andraw算法

把所有点按第一关键字x第二关键字y按从小到大排序,并且删除重复点,得到序列p1…pn

把p1 p2放入凸包中,凸包中的点用栈来保存

然后 p1p2和p2p3叉积,如果叉积大于0,则说明p1p2在p2p3右边,说明p3在内部,我们就不选,反之则把它拖入栈中并且要把p2拖出栈中

这样一直到pn算完成了一遍(下凸包),我们还要从pn反过来到p1(上凸包),合起来就是完整的凸包

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct point{
int x,y;
};
bool cmp(point a,point b)
{
if(a.y==b.y&&a.x<b.x)
return true;
else if(a.y<b.y) return true;
return false;
}
double dis(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(b.y-a.y)*(b.y-a.y));
}
bool xcross(point a,point b,point c)
{
return (a.x-c.x)*(b.y-c.y)>=(b.x-c.x)*(a.y-c.y);//斜率
}
point node[100005];
int num[100005];
int n;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;++i)
{
scanf("%d%d",&node[i].x,&node[i].y);
}
sort(node,node+n,cmp);
num[0]=0; num[1]=1;
int top=1;
for(int i=2;i<n;++i)
{
while(top>1&&xcross(node[i],node[num[top]],node[num[top-1]]))
top--;
top++;
num[top]=i;
}
int basic=top;
for(int i=n-2;i>=0;--i)
{
while(top>basic&&xcross(node[i],node[num[top]],node[num[top-1]]))
top--;
top++;
num[top]=i;
}
double s;
s=0.0;
for(int i=1;i<=top;++i)
{
s+=dis(node[num[i-1]],node[num[i]]);
}
printf("%.1lf",s);
return 0;
}

旋转卡壳

旋转卡壳可以用来求凸包的直径,宽度,两个不相交凸包间最大距离和最小距离等

如果过凸包上的两个点可以画一对平行直线,使凸包上所有点都夹在两
条平行线之间 || 落在平行线上,那么这两个点称为一对对踵点。


其实简单来说就是用一对平行线“卡”住凸包进行旋转。
被一对卡壳正好卡住的对应点对称为对踵点,对锺点的具体定义不好说,不过从图上还是比较好理解的。可以证明对鍾点的个数不超过3*n/2

卡壳有两种情况,第一种是一点对一点, 也就是上图中的

另一种是一边只有一点,另外一边有两个点

第二种情况中我们可以发现对鍾点到对应边的距离比其他的要大(不要问我为什么)

Step1:计算多边形 y 方向上的端点,称之为 ymin 和 ymax。

Step2:通过 ymin 和 ymax 构造两条水平切线,由于他们已经是一对对
踵点,计算他们之间的距离并维护一个当前最大值。

Step3:同时旋转两条直线到其中一条与多边形的一条边重合。

Step4:一个新的对踵点对此时产生,计算新的距离,并和当前最大值进
行比较,若大于当前最大值。则更新。

Step5:重复 Step3 和 Step4 的过程直到再次产生新的对踵点对。

Step6:输出最大直径的对踵点对。

听起来有点小麻烦,观察可以发现当平行线和多边形的一条边重合的时
候最会产生一对新的对踵点
这条边的两个端点和原来的点都可能更新最大值

不妨考虑找离每条边最远的点,显然,这条边的两个端点都和最远点是
对踵点
特殊情况,如果有两条边是平行的,必须考虑所有的对踵点。
旋转卡壳的均摊复杂度 O(n),但这个问题需要求凸包,复杂度是
O(nlogn)

void solve2(int num)
{
int ymax=-1e5,ymin=1e5;
int ymaxidx,yminidx;
for(int i=1;i<=num;i++)
{
if(ch[i].y>ymax)
{
ymax=ch[i].y;
ymaxidx=i;
}
if(ch[i].y<ymin)
{
ymin=ch[i].y;
yminidx=i;
}
}
int ans=dis2(ch[ymaxidx]-ch[yminidx]);
ch[num+1]=ch[1];
for(int t=1;t<=num;t++,yminidx=yminidx%num+1)
{
while(xmult(ch[yminidx+1],ch[ymaxidx+1],ch[yminidx])>xmult(ch[yminidx+1],ch[ymaxidx],ch[yminidx]))ymaxidx=ymaxidx%num+1;
ans=max(ans,dis2(ch[ymaxidx]-ch[yminidx]));
ans=max(ans,dis2(ch[ymaxidx]-ch[yminidx+1]));
}
printf("%d\n",ans);
}