洛谷P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows 计算凸包

https://www.luogu.org/problem/P2742
题目描述

农夫约翰想要建造一个围栏用来围住他的奶牛,可是他资金匮乏。他建造的围栏必须包括他的奶牛喜欢吃草的所有地点。对于给出的这些地点的坐标,计算最短的能够围住这些点的围栏的长度。
输入格式

输入数据的第一行包括一个整数 N。N(0 <= N <= 10,000)表示农夫约翰想要围住的放牧点的数目。接下来 N 行,每行由两个实数组成,Xi 和 Yi,对应平面上的放牧点坐标(-1,000,000 <= Xi,Yi <= 1,000,000)。数字用小数表示。
输出格式

输出必须包括一个实数,表示必须的围栏的长度。答案保留两位小数。
输入输出样例
输入 #1

4
4 8
4 12
5 9.3
7 8

输出 #1

12.00

说明/提示

题目翻译来自NOCOW。

USACO Training Section 5.1

思路:计算出凸包后,再计算出凸包的周长即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;

const double pi=acos(-1);//弧度pi
const double eps=1e-8;//精度

inline int sgn(double x)
{
    if(fabs(x)<eps)
        return 0;
    if(x>0)
        return 1;
    return -1;
}

struct point
{
    double x,y;
    point(double a=0,double b=0)
    {
        x=a,y=b;
    }
    friend point operator * (point a,double b)
    {
        return point(a.x*b,a.y*b);
    }
    friend point operator * (double a,point b)
    {
        return point(b.x*a,b.y*a);
    }
    point operator - (const point &b)const
    {
        return point(x-b.x,y-b.y);
    }
    point operator + (const point &b)const
    {
        return point(x+b.x,y+b.y);
    }
    point operator / (const double b)const
    {
        return point(x/b,y/b);
    }
    bool operator < (const point &b)const//按坐标排序
    {
        if(fabs(x-b.x)<eps)
            return y<b.y-eps;
        return x<b.x-eps;
    }
    bool operator == (const point &b)const
    {
        return sgn(x-b.x)==0&&sgn(y-b.y)==0;
    }
    void transxy(double sinb,double cosb)//逆时针旋转b弧度
    {                                      //若顺时针 在传入的sinb前加个-即可
        double tx=x,ty=y;
        x=tx*cosb-ty*sinb;
        y=tx*sinb+ty*cosb;
    }
    void transxy(double b)//逆时针旋转b弧度
    {                     //若顺时针传入-b即可
        double tx=x,ty=y;
        x=tx*cos(b)-ty*sin(b);
        y=tx*sin(b)+ty*cos(b);
    }
    double norm()
    {
        return sqrt(x*x+y*y);
    }
};

inline double dot(point a,point b)//点积
{
    return a.x*b.x+a.y*b.y;
}
inline double cross(point a,point b)//叉积
{
    return a.x*b.y-a.y*b.x;
}

inline double dist(point a,point b)//两点间距离
{
    return (a-b).norm();
}

typedef point Vector;

vector<point> a;

struct polygon_convex//凸包
{
    vector<point> p;
    polygon_convex(int siz=0)
    {
        p.resize(siz);
    }
    double perimeter()//计算多边形周长
    {
        double sum=0;
        int len=p.size();
        for(int i=0;i<len-1;i++)
            sum+=(p[i+1]-p[i]).norm();
        sum+=(p[0]-p[len-1]).norm();
        return sum;
    }
};

polygon_convex convex_hull()//用a中的点求解出凸包
{
    polygon_convex res(2*a.size()+5);
    sort(a.begin(),a.end());//按照横坐标排序
    a.erase(unique(a.begin(),a.end()),a.end());//去重
    int m=0;
    int len=a.size();
    for(int i=0;i<len;i++)//求下凸包
    {
        while(m>1&&sgn(cross(res.p[m-1]-res.p[m-2],a[i]-res.p[m-2]))<=0)
            --m;
        res.p[m++]=a[i];
    }
    int k=m;
    for(int i=len-2;i>=0;i--)//求上凸包
    {
        while(m>k&&sgn(cross(res.p[m-1]-res.p[m-2],a[i]-res.p[m-2]))<=0)
            --m;
        res.p[m++]=a[i];
    }
    res.p.resize(m);
    if(len>1)//去重
        res.p.resize(m-1);
    return res;
}

int main()
{
    int n;
    point tmp;
    scanf("%d",&n);
    a.resize(n);
    for(int i=0;i<n;i++)
    {
        scanf("%lf%lf",&tmp.x,&tmp.y);
        a[i]=tmp;
    }
    polygon_convex pc=convex_hull();
    printf("%.2f\n",pc.perimeter());
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值