-
前言
未经允许,请勿转载。
凸包真难。
代码如果相似是因为我就是向别人学的。
(为什么没人讲向量..第一次学不懂向量然后代码硬是看不懂嘤嘤嘤..)
-
向量及运算的芝士(可能摘自很多地方..
定义:有大小和方向的量,又称为矢量。
坐标(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)
- (a·b=0) 则(a perp b)
- (a·b<0) 则他们的夹角大于90
- (a·b>0) 则他们的夹角小于90
叉积:(a×b=x_1y_2-x_2y_1)
- 若(a×b=0)则(a)与(b)共线(可以反向)
- 若(a×b>0)则(b)在(a)逆时针方向
- 若(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
",ans);
return 0;
}