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

题目描述

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

输入输出格式

输入格式:

输入数据的第一行包括一个整数 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


听说是二维凸包模板题,就找来做了。计算几何写起来好麻烦啊orz
解题过程注释里感觉写的很详细了,就不废话了,直接放代码好了。
二维凸包详细的计算方法在这里。(不要脸的骗访问哈哈哈qaq)
https://www.cnblogs.com/Amaris-diana/p/10517537.html
//P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 10005;
typedef pair<double, double> _pair;

_pair point[maxn];
_pair In_Bag[maxn];
double Get_Dis (_pair point1, _pair point2)
{
    //计算两点间距离
    return sqrt(((point1.first- point2.first)* (point1.first- point2.first) )
                + ((point1.second- point2.second)* (point1.second- point2.second) ) );
}
double Get_axb (_pair a_point1, _pair a_point2, _pair b_point1, _pair b_point2)
{
    //计算两条向量的叉积
    //向量a= a_point1 --> a_point2= a_point2- a_point1;
    //向量b= b_point1 --> b_point2= b_point2- b_point1;
    //叉积axb= (a.x* b.y)- (b.x* a.y);
    //a.x= a_point2.x- a_point1.x; a.y= a_point2.y- a_point1.y;
    return (((a_point2.first- a_point1.first)* (b_point2.second- b_point1.second) )
            - ((b_point2.first- b_point1.first)* (a_point2.second- a_point1.second) ) );
}
double Get_Cos (_pair point1, _pair point2)
{
    //计算向量a(point1-->point2) 和x轴所成角的余弦值;
    point2.first-= point1.first;                //把point1看作坐标原点(0, 0);
    point2.second-= point1.second;              //则point2的坐标为(P2.x- P1.x, P2.y- P1.y);
    point1.first= 0;
    point1.second= 0;
    _pair point3;                               //在X轴上找一点P3,做直角三角形;
    point3.first= point2.first;                 //P3.x= P2.x;
    point3.second= 0;                           //P3.y= P1.y= 0;
    double Dis_P1_P2= Get_Dis(point1, point2);  //计算直角三角形的斜边长,即P1P2之间的距离;
    return point3.first/ Dis_P1_P2;             //邻边/ 斜边;
}
bool cmpx_0 (_pair a, _pair b)
{
    //小于运算(y,x尽量小);
    if (a.second == b.second) return a.first- b.first< 0;
    return a.second- b.second< 0;
}
bool cmpx_1 (_pair a, _pair b)
{
    //小于运算(按与基点P0所成向量的余弦值大小,余弦值越大越优先;cosx在[0,Pi]内从1到-1,减函数;
    //排序后,按逆时针方向遍历点集;
    _pair tmp = point[0];                       //基点;
    double Cos_a = Get_Cos(tmp, a);             //求出a,b的余弦值;
    double Cos_b = Get_Cos(tmp, b);
    return Cos_a- Cos_b> 0;                     //余弦值越大越优先(越大逆时针遍历越靠前);
}
int main()
{
    int n;
    double x, y;
    while (cin >> n)
    {
        for (int i = 0; i < n; i ++)
        {
            cin >> x >> y;
            if (i )
            {
                if (y< point[0].second|| (y== point[0].second&& x< point[0].first) )
                {
                    double tmp= y;
                    y= point[0].second;
                    point[0].second= tmp;
                    tmp= x;
                    x= point[0].first;
                    point[0].first= tmp;
                }
            }
            point[i].first= x;
            point[i].second= y;
        }
        sort(point+ 1, point+ n, cmpx_1);
        int cnt= -1;                          //cnt -->In_Bag[]中最后一位元素的数组下标;
        In_Bag[++ cnt]= point[0];
        for (int i = 1; i < n; i ++)          //从point[1]开始;
        {
            while (cnt&& Get_axb(In_Bag[cnt- 1], In_Bag[cnt], In_Bag[cnt], point[i])< 0 )
            {
                //当In_Bag中至少有基点和另一点时(cnt>= 1时);
                //逆时针扫描时,如果向量{Pn-1, Pn}与{Pn, Pn+1}的叉积为负,则将上一点删除;
                //(顺时针扫描判断是否为正)
                -- cnt;
            }
            In_Bag[++ cnt]= point[i];
        }
        double Dis = 0;
        for (int i= 0; i<= cnt; i ++)
        {
            Dis+= Get_Dis(In_Bag[i], In_Bag[(i+ 1)% (cnt+ 1)]);
        }
        printf("%.2f\n", Dis);
    }
    return 0;
}

 

posted @ 2019-03-13 16:38  egoist的翻绳游戏  阅读(274)  评论(0编辑  收藏  举报