zoukankan      html  css  js  c++  java
  • 矢量及【模板】二维凸包

    一、矢量

    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
    */
    

    深深的明白自己的渺小

  • 相关阅读:
    [转]面向接口编程详解(二)——编程实例
    [转]面向接口编程详解(一)——思想基础
    [转] LINQ to SQL快速上手 step by step
    Java备份MySQl数据库,并备份图片数据
    用密码密码拦截
    引用 MySQL集群:主从数据库配置 实现查询负载
    Oracle 对表操作 提示:资源正忙,要求指定nowait
    Jquery一款非好的图片轮换效果
    CodeBlocks集成ObjectiveC开发 Windows下学习ObjectiveC
    查询指定库中所有表
  • 原文地址:https://www.cnblogs.com/ShuraEye/p/11354503.html
Copyright © 2011-2022 走看看