矢量及【模板】二维凸包

一、矢量

1.定义:是一条有方向的线段的长度

如图,(a)就是线段(overrightarrow{AB})的矢量

2.三角形法则

如图

(ecause overrightarrow{AB})(=a)(overrightarrow{BC})(=b)
( herefore overrightarrow{AC})(=overrightarrow{AB}+overrightarrow{BC}=a+b)

这就是三角形法则

这是

加法

那么

减法?

由几何意义得
如图

(ecause overrightarrow{BC'}=-overrightarrow{BC})

( herefore overrightarrow{BC'}=-b)

又由三角形法则的加法得

(overrightarrow{AC'}=overrightarrow{AB}+overrightarrow{BC'}=a+(-b)=a-b)

由此得出(a-b)的矢量表示

3.平行四边形法则

这个法则是由三角形法则扩展而成

其实本质和三角形法则一样

如图,由三角形法则得出,不同方向的线段相加的结果

在这里引入

矢量的叉乘

如图

(P=overrightarrow{OP},Q=overrightarrow{OQ})
(P imes Q=)(egin{vmatrix} x1 & y1 \ x2 & y2 end{vmatrix})

也就是(P imes Q=x1 imes y2-x2 imes y1)

同时(Q imes P=x2 imes y1-x1 imes y2)

因此我们可以得到(P imes Q=-(Q imes P))

所以可以分类讨论得

  1. (P imes Q>0)(overrightarrow{OP})(overrightarrow{OQ})的顺时针方向

  2. (P imes Q<0)(overrightarrow{OP})(overrightarrow{OQ})的逆时针方向

  3. (P imes Q=0)(overrightarrow{OP},overrightarrow{OQ})共线

由此推得

右手法则:一个简单的确定向量的方向的方法是这样的:右手的四指从a以不超过180度的转角转向b时,竖起的大拇指指向是c的方向。

则对于有公共端点的线段(overrightarrow{P_{0}P_{1}})(overrightarrow{P_{1}P_{2}}),通过计算((P_{2}-P_{0}) imes (P_{1}-P_{0}))的符号确定折线方向(重点)

因此,可以用这个方法来做凸包

二、凸包

1. 定义:凸包是啥?就是给你一堆点,然后用一根有弹性的橡皮筋包住所有的点,这个橡皮筋就是一个凸包。题目可能会让你求构成凸包的点或者是凸包的长度或围成的面积


2. 怎么求凸包

第一步:

在这里,我们要求(y)值最小的点,我们可以用(sort)排序


(cmp:)

bool cmp(edge a,edge b)
{
    if(a.y==b.y)return a.x<b.x;
    else return a.y<b.y;
}

for(int i=1;i<=n;i++)scanf("%lf %lf",&tain[i].x,&tain[i].y);
sort(tain+1,tain+1+n,cmp);

第二步:

在图中,我们可以看到,我们要选择的点最优是极角最小的点,也是我们每次都要往顺时针方向走,所以我们在一开始排完序后,要进行极角排序,再进行顺逆时针判断,满足是顺时针的最小极角就是我们要的点

极角排序有两种方法:

bool cmp(node a,node b)//极角排序另一种方法,速度快
{
    if(atan2(a.y-yy,a.x-xx)!=atan2(b.y-yy,b.x-xx))
        return (atan2(a.y-yy,a.x-xx))<(atan2(b.y-yy,b.x-xx));
    return a.x<b.x;
}
//atan2是一个函数,返回的是原点至点(x,y)的方位角,即与 x 轴的夹角。
------------

bool cmp(node a,node b)//极角排序普通
{
    int m=cross(vex[0],a,b);
    if(m>0)
        return 1;
    else if(m==0&&dis(vex[0],a)-dis(vex[0],b)<=0)
        return 1;
    else return 0;
}

接下来就是循环,寻找满足在逆时针的点,这就涉及到求叉积


求叉积:

double cross(edge a,edge b,edge c)
{
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}

循环:

for(int i=3;i<=n;i++)
{
    while(cross(convex[tot-1],convex[tot],tain[i])<0)tot--;
    convex[++tot]=tain[i];
}

后面的就比较基础,不仔细讲了

放代码:

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n;
struct edge
{
    double x,y;
};
edge tain[N],convex[N];
double xx,yy;
double dis(edge a,edge b)//两点直线距离公式
{
    return sqrt((a.x-b.x)*(a.x-b.x)*1.0+(a.y-b.y)*(a.y-b.y));
}
double cross(edge a,edge b,edge c)//计算叉积
{
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
bool cmp(edge a,edge b)
{
    if(a.y==b.y)return a.x<b.x;
    else return a.y<b.y;
}
bool cmp1(edge a,edge b)
{
    if(atan2(a.y-yy,a.x-xx)!=atan2(b.y-yy,b.x-xx))
        return atan2(a.y-yy,a.x-xx)<atan2(b.y-yy,b.x-xx);
    return a.x<b.x;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lf %lf",&tain[i].x,&tain[i].y);
    sort(tain+1,tain+1+n,cmp);
    convex[1]=tain[1];
    xx=convex[1].x;
    yy=convex[1].y;
    sort(tain+2,tain+1+n,cmp1);
    int tot=2;
    convex[2]=tain[2];
    for(int i=3;i<=n;i++)
    {
        while(cross(convex[tot-1],convex[tot],tain[i])<0)tot--;
        convex[++tot]=tain[i];
    }
    double ans=0;
    for(int i=2;i<=tot;i++)
        ans+=dis(convex[i-1],convex[i]);
    ans+=dis(convex[1],convex[tot]);
    printf("%.2lf",ans);
    return 0;
}
/*
4
4 8
4 12
5 9.3
7 8
*/

深深的明白自己的渺小

原文地址:https://www.cnblogs.com/ShuraEye/p/11354503.html