「凸包」学习笔记

  • 前言

    未经允许,请勿转载。

凸包真难。

代码如果相似是因为我就是向别人学的。

(为什么没人讲向量..第一次学不懂向量然后代码硬是看不懂嘤嘤嘤..)


  • 向量及运算的芝士(可能摘自很多地方..

    定义:有大小方向的量,又称为矢量

    坐标\(A(x_1,y_1),B(x_2,y_2)\)

    向量\(\overrightarrow{AB}=(x_2-x_1,y_2-y_1)\)

    加法:\((x_1+x_2,y_1+y_2)\)

    减法:\((x_1-x_2,y_1-y_2)\)

    点积:\(a·b=x_1x_2+y_1y_2\)

    1. \(a·b=0\)\(a \perp b\)
    2. \(a·b<0\) 则他们的夹角大于90
    3. \(a·b>0\) 则他们的夹角小于90

    叉积:\(a×b=x_1y_2-x_2y_1\)

    1. \(a×b=0\)\(a\)\(b\)共线(可以反向)
    2. \(a×b>0\)\(b\)\(a\)逆时针方向
    3. \(a×b<0\)\(b\)\(a\)顺时针方向

  • 有啥用?求什么

    给出一堆点,再用一根绳子包住,这就是个凸包啦。

    然后一般问绳子的长度。


  • 如何实现?

    主要分为2部:求上半部分和下半部分。

    首先,先对每一个点以\(x\)坐标从小到大排序,如果一样再以\(y\)坐标从小到大排序。

    比如上面那个例子:

    初始化\((1)(2)\)放入栈中,此时栈中有\((1)(2)\)

    然后把\((3)\)放入栈中,此时栈中有\((1)(2)\)

    因为此时在求下半部分,因此\((3)\)我们希望越下越好,即\(\overrightarrow{(1)(2)}×\overrightarrow{(2)(3)} \geqslant0\),(叉积的定义),然后可以发现\((3)\)满足的:

    所以栈不改变,栈中有\((1)(2)(3)\)

    然后,把\((4)\)放入栈中,\((4)\)满足\(\overrightarrow{(2)(3)}×\overrightarrow{(3)(4)} \geqslant0\),所以栈不改变:\((1)(2)(3)(4)\)

    然后把\((5)\)放入栈中,发现\((5)\)不满足\(\overrightarrow{(3)(4)}×\overrightarrow{(4)(5)} \geqslant0\),所以把\((4)\)弹出栈

    而出栈后发现,\((5)\)不满足\(\overrightarrow{(2)(3)}×\overrightarrow{(3)(5)} \geqslant0\),所以把\((3)\)踢出栈。此时栈中剩下:\((1)(2)(5)\)

    \((6)\)放入栈,\((6)\)满足\(\overrightarrow{(2)(5)}×\overrightarrow{(5)(6)} \geqslant0\),不改变栈:\((1)(2)(5)(6)\)

    \((7)\)放入栈,\((7)\)满足\(\overrightarrow{(5)(6)}×\overrightarrow{(6)(7)} \geqslant0\),不改变栈:\((1)(2)(5)(6)(7)\)

    注:可能画歪了..\((6)(7)\)\(y\)坐标相同

    \((8)\)放入栈,\((8)\)不满足\(\overrightarrow{(6)(7)}×\overrightarrow{(7)(8)} \geqslant0\),所以把\((7)\)踢出\((1)(2)(5)(6)(8)\)

    但是发现,此时仍不满足\(\overrightarrow{(5)(6)}×\overrightarrow{(6)(8)} \geqslant0\),所以再把\((6)\)踢出,栈中剩下:\((1)(2)(5)(8)\)

    这样,下部分的凸包就完成啦,而上部分反着做一遍就好了。


  • 代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
struct point{
	double x,y;
}p[100010];
int n,sta[100010],cnt;
double ans;
point getvec(point a,point b){return point{(b.x-a.x),(b.y-a.y)};}
double dis(point a,point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
double xmul(point a,point b){return a.x*b.y-a.y*b.x;}
bool cmp(point a,point b)
{	
		if(a.x==b.x)return a.y<b.y;
		return a.x<b.x;
}
int main()
{	
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%lf%lf",&p[i].x,&p[i].y);
		sort(p+1,p+1+n,cmp);
		sta[++cnt]=1;sta[++cnt]=2;
		for(int i=3;i<=n;i++)
		{	
			point u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
			point v=getvec(p[sta[cnt]],p[i]);
			while(xmul(u,v)<0.0)
			{	
				if(cnt==1)break;
				cnt--;
				u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
				v=getvec(p[sta[cnt]],p[i]);
			}
			sta[++cnt]=i;
		}
		int tmp=cnt;
		sta[++cnt]=n;sta[++cnt]=n-1;
		for(int i=n-2;i>=1;i--)
		{	
			point u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
			point v=getvec(p[sta[cnt]],p[i]);
			while(xmul(u,v)<0.0)
			{	
				if(cnt==tmp+1)break;
				cnt--;
				u=getvec(p[sta[cnt-1]],p[sta[cnt]]);
				v=getvec(p[sta[cnt]],p[i]);
			}
			sta[++cnt]=i;
		}
		for(int i=1;i<=cnt-1;i++)
			ans+=dis(p[sta[i]],p[sta[i+1]]);
		printf("%.2lf\n",ans); 
		return 0;
}
posted @ 2020-02-01 21:58  Rainy7  阅读(177)  评论(0编辑  收藏  举报