洛谷 P2742 [USACO5.1]圈奶牛Fencing the Cows
题目描述
农夫约翰想要建造一个围栏用来围住他的奶牛,可是他资金匮乏。他建造的围栏必须包括他的奶牛喜欢吃草的所有地点。对于给出的这些地点的坐标,计算最短的能够围住这些点的围栏的长度。
输入输出格式
输入格式:
输入数据的第一行包括一个整数 N。N(0 <= N <= 10,000)表示农夫约翰想要围住的放牧点的数目。接下来 N 行,每行由两个实数组成,Xi 和 Yi,对应平面上的放牧点坐标(-1,000,000 <= Xi,Yi <= 1,000,000)。数字用小数表示。
输出格式:
输出必须包括一个实数,表示必须的围栏的长度。答案保留两位小数。
输入输出样例
说明
题目翻译来自NOCOW。
USACO Training Section 5.1
题目求的就是凸包的周长,求出凸包以后算一遍就行了。
(下面是我打了一半的二维几何的板子)
/* 这里用来存坑: 1.利用叉积求和三角形面积有关的别忘了除二 2.还差旋转卡壳和半平面交 */ #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<ctime> #define ll long long #define maxn 10005 using namespace std; const double eps=0.000000001; bool flag=0; inline int zt(double x){ if(fabs(x)<=eps) return 0; if(x>0) return 1; return -1; } struct node{ double x,y; //极角 double omega; //数乘 node operator *(const double& u)const{ return (node){x*u,y*u}; } //点+向量 or 向量+向量 (点+点没有意义) node operator +(const node& a)const{ return (node){x+a.x,y+a.y}; } //点-点得到向量 node operator -(const node& a)const{ return (node){x-a.x,y-a.y}; } //为了按极角排序 bool operator <(const node& a)const{ return zt(omega-a.omega)==-1; } }c[maxn],q[maxn],ret[maxn]; //按x第一关键字,y第二关键字升序排序 bool cmp(node x,node y){ if(!zt(x.x-y.x)) return zt(x.y-y.y)<0; else return zt(x.x-y.x)<0; } //判断两个点是否重合 bool eq(node x,node y){ return (!zt(x.x-y.x)&&!zt(x.y-y.y)); } //点积 inline double pointmul(node x,node y){ return x.x*y.x+x.y*y.y; } //叉积 inline double Xmul(node x,node y){ return x.x*y.y-x.y*y.x; } //两个向量夹角 inline double getthita(node x,node y){ double now=pointmul(x,y)/pointmul(x,x)/pointmul(y,y); //acos和asin损失精度比较厉害,所以可以转化成点之后用atan2 return atan2(sqrt(1-now*now),now); } //求一个向量旋转thita角之后得到的向量 inline node rotate(node l,double thita){ double co=cos(thita),si=sin(thita); return (node){l.x*co-l.y*si,l.x*si+l.y*co}; } //两点间距离 inline double dist(node x,node y){ return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y)); } struct lines{ //点向法表示直线 node base,vec; }; //两直线交点 inline node pub(lines a,lines b){ return a.base+a.vec*fabs(Xmul(a.base-b.base,b.vec)/Xmul(a.vec,b.vec)); } //求多边形(可以不凸,但是各边不能乱交) inline double S(node *u,int len){ if(!flag){ //如果不是求出凸包后得到的多边形的话还需要按照极角排序。 //不过如果是求出的凸包之后的多边形极角已经是有序的了。 for(int i=1;i<=len;i++) u[i].omega=atan2(u[i].y,u[i].x); sort(u+1,u+len+1); } double an=0; for(int i=2;i<=len;i++) an+=Xmul(u[i-1],u[i]); an+=Xmul(u[len],u[1]); return an/2.00; } //把点集u变成一个凸包(而且极角已经有序),并返回凸包上点的个数 inline int get_hill(node *u,int len){ //求凸包的话得按x和y坐标排序 sort(u+1,u+len+1,cmp); int tt=0,tot=0; //下凸包 q[1]=u[1],q[2]=u[2],tt=2; for(int i=3;i<=len;i++){ while(tt>1&&zt(Xmul(q[tt]-q[tt-1],u[i]-q[tt]))<0) tt--; q[++tt]=u[i]; } for(int i=1;i<=tt;i++) ret[++tot]=q[i]; //上凸包 q[1]=u[1],q[2]=u[2],tt=2; for(int i=3;i<=len;i++){ while(tt>1&&zt(Xmul(q[tt]-q[tt-1],u[i]-q[tt]))>0) tt--; q[++tt]=u[i]; } for(int i=tt;i;i--) if(!eq(q[i],ret[tot])&&!eq(q[i],ret[1])) ret[++tot]=q[i]; for(int i=1;i<=tot;i++) u[i]=ret[i]; flag=1; return tot; } int n; double B; inline void solve(int problem_id){ if(problem_id==2785){ scanf("%d%lf",&n,&B); for(int i=1;i<=n;i++) scanf("%lf%lf",&c[i].x,&c[i].y); printf("%.4lf\n",B*S(c,n)); } if(problem_id==2742){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lf%lf",&c[i].x,&c[i].y); n=get_hill(c,n); double ans=0; for(int i=2;i<=n;i++) ans+=dist(c[i],c[i-1]); ans+=dist(c[1],c[n]); printf("%.2lf\n",ans); } } int main(){ srand(time(0)); solve(rand()%1+2742); return 0; }
我爱学习,学习使我快乐