HDU 1392 Surround the Trees【凸包】

1.题目
题目链接:点这儿!
2.解决方法
在一个二维平面上给出一个点集P,用一根橡皮筋将这些点捆住,形成的也就是一个由P组成的最小多边形,这个凸多边形被称作凸包;求凸包的周长,常见的有Graham扫描法、Andrew算法;后者是前者的改进,本题使用Andrew算法。

Andrew算法:将所有点按照x升序排序,若是x相等,则以y进行升序排序。从最左下方的点开始,求下凸壳;再从最右上方开始,求上凸壳。上下凸壳长度之和即为凸包周长。求凸壳的每一步,尽量将栈最上方两点构成的向量“逆时针旋转”。

3.代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN=110;
const double eps=1e-8;

struct Node{
    double x,y;

    bool operator <(const Node& A){
        if(x!=A.x) return x<A.x;
        return y<A.y;
    }

    bool operator ==(const Node& A){
        return A.x==x&&A.y==y;
    }

}s[MAXN];
int n;


Node stack[MAXN];

double Cross(Node O,Node A,Node B)//求向量OA与向量OB的叉积
{
    return (double)((A.x-O.x)*(B.y-O.y)-(double)(B.x-O.x)*(A.y-O.y));
}

double Distance(Node A,Node B)
{
    return (double)sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

int sgn(double x)//判断x的正负性
{
    if(fabs(x)<eps) return 0;
    else return x<0?-1:1;
}

int main(void)
{
    while(cin>>n&&n){

        for(int i=0;i<n;i++) cin>>s[i].x>>s[i].y;

        //Andrew算法
        sort(s,s+n);
        n=unique(s,s+n)-s;//删去重复点

        //求下凸壳
        int top=0;
        for(int i=0;i<n;i++){//叉积值为正,则将点B加入栈,否则说明当前栈顶A点不是凸包上的点,出栈
            while(top>1&&sgn(Cross(stack[top-2],stack[top-1],s[i]))<=0)
                top--;
            stack[top++]=s[i];
        }

        //再求上凸壳
        int j=top;
        for(int i=n-2;i>=0;i--){
            while(top>j&&sgn(Cross(stack[top-2],stack[top-1],s[i]))<=0) top--;
            stack[top++]=s[i];
        }

        if(n>1) top--;//避免重复计入最后一个点

        double ans=0.0;
        if(top==1)
            ans=0.0;
        else if(top==2)
            ans=Distance(stack[top-2],stack[top-1]);
        else{
            for(int i=0;i<top;i++)//计算周长
                ans+=Distance(stack[i],stack[(i+1)%top]);
        }
        printf("%.2lf\n",ans);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值