凸包入门之卷包裹法 & hdu 1348 wall

在二维空间中,凸包可以简单的认为是最小的包含所有点的凸多边形。
简单的卷包裹法:寻找最边缘(最下方的,次之是最左边的;或者最左边的,次之最下边)点。假想用一根绳子向右逆时针旋转碰到另一个点,这样新找到的点作为端点,继续旋转绳子重复找点的步骤,一直围成一个凸多边形。时间复杂度:O(n^2)     //我把此代码和Graham-scan比较了一下,没有理解为什么他就是 O(n^2) T_T
(如果在寻找的射线上有多个点的情况,使用和保留离前端点最远的那一点)

因为涉及到旋转和夹角,所以运用到了叉积。

例子:

hdu 1348  wall

http://acm.hdu.edu.cn/showproblem.php?pid=1348

大致题意:给出N个点,它所形成的多边形向外撑长L,新的多边形的周长是多少?

分析:每对对顶角的和是180度,n多边形有n个顶点,同时多边形的内角和是180(n-2),那么外围的夹角和就应该是180n-180(n-2)=360

于是得到结论:多边形的周长+圆的周长就是所求。

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
struct point{
    int x,y;
}pt[1005];
int sta[1005],ans[1005],cnt;
int cmp(point a,point b){
    //左下角优先 先左再下
    return a.x<b.x||(a.x==b.x&&a.y<b.y);  
}
int cross(point p0,point p1,point p2){  //大于0, 逆时针
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
void convex(int n){
    sort(pt,pt+n,cmp);
    cnt=0;
    int top=0;
    sta[top++]=0; sta[top++]=1; //下凸包
    for(int i=2;i<n;i++){
        while(top>1&&cross(pt[sta[top-2]],pt[sta[top-1]],pt[i])<=0) {
            top--;
        }
        sta[top++]=i;
    }
    for(int i=0;i<top;i++) ans[cnt++]=sta[i];

    top=0;
    sta[top++]=n-1;  sta[top++]=n-2; //求上凸包 (将pt[n-1]和pt[0]连接起来。)
    for(int i=n-3;i>=0;i--){
        while(top>1&&cross(pt[sta[top-2]],pt[sta[top-1]],pt[i])<=0){
            top--;
        }
        sta[top++]=i;
    }
    for(int i=0;i<top;i++) ans[cnt++]=sta[i];
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int n,l,t;
    double Pi=acos(0.0)*2;
    cin>>t;
	while(t--){
        scanf("%d%d",&n,&l);
		for(int i=0;i<n;i++){
            scanf("%d%d",&pt[i].x,&pt[i].y);
        }
        convex(n);
        double res=2*Pi*l;
        for(int i=0;i<cnt-1;i++){
            res+=sqrt(1.0*(pt[ans[i]].x-pt[ans[i+1]].x)*(pt[ans[i]].x-pt[ans[i+1]].x)
                      +(pt[ans[i]].y-pt[ans[i+1]].y)*(pt[ans[i]].y-pt[ans[i+1]].y));
        }
        printf("%.0lf\n",res);
        if(t)  puts("");
    }
    return 0;
}

再来模仿一种写法:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
struct point{
    int x,y;
}pt[1005],ans[1005];
int cnt;
int cmp(point a,point b){
    if(a.x==b.x) return a.y<b.y;  //左下角优先 先左再下
    return a.x<b.x;    
}
int cross(point p0,point p1,point p2){  //大于0, 逆时针
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double length(point a,point b){
    return sqrt(1.0*(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void convex(int n){
    sort(pt,pt+n,cmp);
    cnt=0;
    for(int i=0;i<n;i++){ //下凸包
        while(cnt>1&&cross(ans[cnt-2],ans[cnt-1],pt[i])<=0) {
            cnt--;
        }
        ans[cnt++]=pt[i];
    }

    int key=cnt;
    //求上凸包 (将pt[n-1]和pt[0]连接起来。)
    for(int i=n-2;i>=0;i--){
        while(cnt>key&&cross(ans[cnt-2],ans[cnt-1],pt[i])<=0){
            cnt--;
        }
        ans[cnt++]=pt[i];
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int n,l;
    double Pi=atan(1.0)*4;
    int t=0;
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&l);
        for(int i=0;i<n;i++){
            scanf("%d%d",&pt[i].x,&pt[i].y);
        }
        convex(n);
        double res=2*Pi*l;
        for(int i=0;i<cnt-1;i++){
            res+=length(ans[i],ans[i+1]);
        }
        printf("%.0lf\n",res);
        if(t) puts("");
    }
    return 0;
}



你可能感兴趣的:(凸包)