zoukankan      html  css  js  c++  java
  • 【学习笔记】凸包

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

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

    20210305更新:po学了向量回来了发现这写的什么玩意,已更新完毕,您可从更新部分跳至凸包部分。同时,原“前置知识:叉积”部分已弃用并放置于最后。

    更新

    以下内容大部分在人教A版数学必修2第一章中涉及。
    向量是什么,你可以不必知道,只需知道其有两个要素:方向和大小,它可以用有向线段表示。下文中我们将写作(vec{x})(vec{AB})
    我们先解释一下向量的坐标:将这条有向线段的起点置于 ((0,0)),则该线段的终点坐标即为该向量的坐标。
    再解释一下向量的共线判定,用数乘表示则是:对于向量(vec{a}),(vec{b}),存在一个实数(lambda),使得(lambda vec{a} = vec{b})。而引入坐标后,设(vec{a})坐标为((a_x,a_y))(vec{b})坐标为((b_x,b_y)),则若两向量共线,则有
    (huge a_x b_y-a_y b_x=0)
    这个形式便是叉积。类似的,通过画图,我们可以得出(vec{a})(vec{b})的关系 对应 叉积与零的关系。(注:我们定义若一个向量(vec{a}),能通过顺时针旋转小于(frac{pi}{2})(vec{b})所在的直线重合,则(vec{b})(vec{a})的右边。反之同理。)
    先给出结论,(vec{a})(vec{b})叉积小于零,则(vec{b})(vec{a})的左边
    需要注意的是,下文中,我们的原点不一定是((0,0)),而可能是其他点。而对应的,坐标也并非相对于((0,0)),而是我们定义的,会改变的“原点”。一般的,按照下图的情况,我们将会定义原点为(P)

    凸包初讲

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

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

    代码实现

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

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

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

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

    以上面图为例,我们首先将(a)(P)入栈,到(b)时,发现(vec{aP})(vec{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;
    } 
    

    分割线
    以下为弃用部分。

    前置知识:叉积

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

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

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

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

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

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

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

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

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

    (a imes b >0)(b)(a)的右边

    (a imes b <0)(b)(a)的左边

    (a imes b =0)则两点共线

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

  • 相关阅读:
    Python使用Django创建第一个项目
    如何使用RobotFramework编写好的测试用例
    接口测试 总结(什么是接口测试)
    APP接口测试
    Django基础五之中间件
    Django基础五之django模型层(二)多表操作
    Django基础五之django模型层(一)单表操作
    Django基础四之模板系统
    Django基础三之视图函数
    DJANGO2--url路由
  • 原文地址:https://www.cnblogs.com/fmj123/p/11326246.html
Copyright © 2011-2022 走看看