【模板】二维凸包
问题描述
二维凸包问题,即求平面上一些点的凸包。凸包,是由平面上一些点连线组成的凸多边形,能够包含平面上所有给定点。本文就是求出凸包上的点是哪些。
!例题
Graham算法
1.将所有点按照纵坐标从小到大排序,然后从纵坐标最小的点开始(若有相同的则从横坐标最小的点开始)
怎么求极角呢?用atan2(y0,x0)。这个函数的返回值是直线 $$y=\frac{y0}{x0} * x$$ 斜率的反正切,是一个弧度制角度,值域是$ (-\pi,\ph]$
2.以上述点为起点,其余点为终点,按得到的向量的极角从小到大排序,然后逆时针处理,如图
3.接下来准备一个数组(有点类似于单调队列但是只有一端操作),塞进第一个点,然后挨个把剩下的点往里试塞,用于维护一个凸壳。凸壳是凸多边形的一部分。
如果插入一个点后图形仍是凸包,就直接入队,否则弹出队尾元素直到满足条件。比如现在队列里有(G,C,I,E,H,B),并且是一个凸壳,接下来想让 F 点入队,发现FB这条线一加就是凹边形了,不行,所以让点B退出队列。然后发现现在可以了,把 F 点入队。
现在的问题就是如何判断凹角了。由于我们是逆时针处理的,所以维护这个凸壳的时候,一直是在左转、左转、左转...永远是在上一条射线的左边找寻新点的,所以我们可以求一个差角。根据我们高中的知识,很容易发现:
对于对内倒数第二个点指向最后一个点的向量 u ,和最后一个点指向新点的向量 v ,计算二者与 x 轴正方向的夹角,记作∠1,∠2,当 ∠2 - ∠1为一个正值时,相当于是‘向左转’,否则是向右转。
代码实现
int n, begin;
struct point{
double x, y;
int id;
}pt[100010];
bool cmpy(point a, point b){
return (a.y == b.y) ? (a.x < b.x) : (a.y < b.y);
}
bool cmpjiao(point a, point b){
return atan2(a.y, a.x) < atan2(b.y, b.x);
}
int stk[100010], h, t;
double ans;
double dis(int i, int j){
return sqrt((pt[i].x - pt[j].x)*(pt[i].x - pt[j].x) + (pt[i].y - pt[j].y)*(pt[i].y - pt[j].y));
}
bool wrong(int a, int b, int c){
double tan_1 = atan2(pt[b].y - pt[c].y, pt[b].x - pt[c].x),
tan_2 = atan2(pt[a].y - pt[b].y, pt[a].x - pt[a].x);
double u_x = pt[b].x - pt[c].x, u_y = pt[b].y - pt[c].y,
v_x = pt[a].x - pt[b].x, v_y = pt[a].y - pt[b].y;
double cos_1 = u_x / sqrt(u_x * u_x + u_y * u_y),
sin_1 = u_y / sqrt(u_x * u_x + u_y * u_y),
cos_2 = v_x / sqrt(v_x * v_x + v_y * v_y),
sin_2 = v_y / sqrt(v_x * v_x + v_y * v_y);
double sin_3 = sin_2 * cos_1 - sin_1 * cos_2;
return (sin_3 >= 0) ? 0 : 1;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf("%lf%lf", &pt[i].x, &pt[i].y);
pt[i].id = i;
}
sort(pt + 1, pt + 1 + n, cmpy);
for(int i = 2; i <= n; ++i){
pt[i].x -= pt[1].x;
pt[i].y -= pt[1].y;
}
pt[1].x -= pt[1].x; pt[1].y -= pt[1].y;
sort(pt + 2, pt + 1 + n, cmpjiao);
stk[h = t = 1] = 1;
for(int i = 2; i <= n; ++i){
while(t > h && wrong(i,stk[t],stk[t - 1]))
t--;
stk[++t] = i;
}
stk[0] = stk[t];
for(int i = 1; i <= t; ++i){
ans += dis(stk[i - 1], stk[i]);
}
printf("%.2lf\n", ans);
return 0;
}
本文来自博客园,作者:咕咕坤,转载请注明原文链接:https://www.cnblogs.com/GuguKun/p/14899512.html