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;
}