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

题目描述

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

输入格式

输入数据的第一行是一个整数。表示农夫约翰想要围住的放牧点的数目 nn。

第 22 到第 (n + 1)(n+1) 行,每行两个实数,第 (i + 1)(i+1) 行的实数 x_i, y_ixi,yi 分别代表第 ii 个放牧点的横纵坐标。

输出格式

输出输出一行一个四舍五入保留两位小数的实数,代表围栏的长度。

输入输出样例

输入 #1
4
4 8
4 12
5 9.3
7 8
输出 #1
12.00

说明/提示

数据规模与约定

对于 100\%100% 的数据,保证 1 leq n leq 10^51n105,-10^6 leq x_i, y_i leq 10^6106xi,yi106。小数点后最多有 22 位数字。

代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+7;
struct node
{
    double x,y;
}p[maxn],s[maxn];//p用于存原始点,s用于可能存在凸壳上的点并用来模拟栈

double d(node a,node b)//距离
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double check(node a1,node a2,node b1,node b2)//叉积(大于零时为逆时针,等于零平行,小于零顺时针)
{
    return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);
}

int cmp(node a,node b)//按照极角的大小逆时针排序(与x轴正方向)
{
    if(check(p[1],a,p[1],b)>0)return 1;
    if(check(p[1],a,p[1],b)==0&&d(p[0],a)<d(p[0],b))return 1;
    return 0;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&p[i].x,&p[i].y);
        if(i!=1&&p[i].y<p[1].y)swap(p[i],p[1]);//取y最小的点作为凸包的第一个点
    }
    sort(p+2,p+n+1,cmp);
    s[1]=p[1];//y最小的点一定在凸壳上
    int cnt=1;
    for(int i=2;i<=n;i++)
    {
        while(cnt>1&&check(s[cnt-1],s[cnt],s[cnt],p[i])<=0)cnt--;//当前点如果与在它之前加入凸壳的前两个点形成了一个朝左的凹壳,则说明中间的那个点可以不要
        cnt++;
        s[cnt]=p[i];//入栈
    }
    s[cnt+1]=p[1];//回到最初点
    double ans=0;
    for(int i=1;i<=cnt;i++)ans+=d(s[i],s[i+1]);
    printf("%.2f",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/chuliyou/p/14163374.html