【学习笔记】凸包

想学斜率优化,先把先决条件凸包给肝了。然后完全不懂,于是就咕了。顺便看了一下线代(其实并不会)

讲真这东西耗了我半个暑假

前置知识:叉积

这个属于线性代数,有兴趣的请上B站学习。推荐链接

叉积是一种在[向量空间]中向量的[二元运算]——百度百科

下文中将使用\(*\)代指乘,用\(\times\)代指叉积

根据某些线代知识,叉积在几何意义上代表的是\(a\),\(b\)两个向量为两条边的平行四边形的面积。

而叉积的运算公式(简略版)为

\(\huge a.x*b.y-a.y*b.x\)

注意,这里的\(x\)\(y\),都是相对于某一点而言的。例如,下图的\(x\)\(y\)相对于点\(P\)而言,而不是相对于点\(O\)而言。

1516098-20190809113910184-432968547.png

求出叉积后,我们可以借助其来判断两个点的相对位置

我们定义若一个向量\(x\),能通过顺时针旋转小于\(180\)度角与\(y\)所在的直线重合,则\(y\)\(x\)的右边。反之同理。则叉积与方向的关系为

\(a \times b >0\)\(b\)\(a\)的右边

\(a \times b <0\)\(b\)\(a\)的左边

\(a \times b =0\)则两点共线

关于叉积,本文仅介绍到这里,若想知道更多关于叉积的信息,请自行搜索。

凸包初讲

我们定义,在平面上能包含所有给定点的最小凸多边形叫做凸包。 凸包的周长最小。

简单的说,平面上有一些点,你要用一个凸多边形把它们围起来。并且这个多边形最小。

代码实现

这里介绍一种实现凸包的算法。

首先我们可以将所有点按照其\(x\)坐标和\(y\)坐标做双关键字排序。然后我们分别维护上凸壳和下凸壳,维护凸壳我们用单调栈实现。显然第一个点和最末点一定在凸包上,所以我们可以从前往后找可以维护上凸壳,从后往前找可以维护下凸壳。

根据凸多边形的定义,若点\(y\)在点\(x\)的右边,则线段\(xy\)则一定不在凸包上

根据这个原理,我们在单调栈维护点时,若找到这种不凸的点,则将其弹出。直到凸了才将点入栈。要注意的是,判断凸不凸至少需要3个点,即栈中至少要有2个点

以上面图为例,我们首先将\(a\)\(P\)入栈,到\(b\)时,发现\(aP\)\(Pb\)的叉积大于0,即\(b\)\(a\)点右边,这显然不合法,于是将\(P\)弹出,此时栈中仅有\(a\)一个点,将\(b\)入栈。

对于共线的情况,可以灵活处理。

当我们正着做一遍,倒着做一遍,凸包就求出来了。

例题

本题显然求的是凸包的周长

例题代码

#include<bits/stdc++.h>
using namespace std;
double ans;
int n,q[10100];
struct point
{
    double x,y;
}p[10100];
bool cmp(point x,point y)
{
     return x.x<y.x||(x.x==y.x&&x.y<y.y);
}
bool dow(point x,point y,point z)
{
    double s=(x.x-z.x)*(y.y-z.y)-(x.y-z.y)*(y.x-z.x);
    if (s<0) return true;
    if (s>=0) return false;
}//叉积
double len(point x,point y)
{
    return sqrt((x.x-y.x) * (x.x-y.x) + (x.y-y.y) *(x.y-y.y));
}//长度计算
int main()
{
    cin>>n;
    for (int i=1;i<=n;i++)
    {
        cin>>p[i].x>>p[i].y;
    }
    sort(p+1,p+1+n,cmp);
    //=====上凸壳====
    int h=0,t=1;
    q[1]=1;
    for (int i=2;i<=n;i++)
    {
        while (t>1&&dow(p[i],p[q[t-1]],p[q[t]]))
        {
            t--;
        }
        q[++t]=i;
    }
    //===下凸壳====
    h=t-1;
    for (int i=n-1;i>0;i--)
    {
        while (h+1<t&&dow(p[i],p[q[t-1]],p[q[t]]))
        {
            t--;
        }
        q[++t]=i;
    }
    for (int i=2 ;i<=t;i++)
    {
        ans+=len(p[q[i]],p[q[i-1]]);
    }
    ans+=len(p[q[t]],p[q[1]]);
    printf("%.2f",ans);
    return 0;
} 

转载于:https://www.cnblogs.com/fmj123/p/11326246.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值