题目描述
农夫约翰想要建造一个围栏用来围住他的奶牛,可是他资金匮乏。他建造的围栏必须包括他的奶牛喜欢吃草的所有地点。对于给出的这些地点的坐标,计算最短的能够围住这些点的围栏的长度。
输入格式
输入数据的第一行是一个整数。表示农夫约翰想要围住的放牧点的数目 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^51≤n≤105,-10^6 leq x_i, y_i leq 10^6−106≤xi,yi≤106。小数点后最多有 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; }