计算几何入门——凸包【Fencing the Cows 圈奶牛】
【Fencing the Cows 圈奶牛】
凸包
题目源自USACOTraining Section 5.1
前奏
向量
点积
叉积
凸包(graham)
方法:
先选纵坐标最小,若相同横坐标最小的点作为初始节点,显然这个点在凸包上。
然后把剩余点按逆时针的角度排序,一个一个加入。
若加入与上一条边顺时针旋转(凹下去了),则弹出,直到顺时针旋转停止。
加入下一个点。
题解
凸包板子(方法上面有),最后求一下边长即可。
#include<bits/stdc++.h>
using namespace std;
#define eps (1e-5)
const int N=1e4+5;
int n,m=0;
struct point{
double x,y;
point(){}
point(int a,int b):x(a),y(b){};
friend inline point operator -(const point &a,const point &b){//向量ba
return point(a.x-b.x,a.y-b.y);
}
friend inline double operator *(const point &a,const point &b){//叉积
return (a.x*b.y-a.y*b.x);
}
inline double calc()const{
return x*x+y*y;
}
}p[N],q[N];
inline bool comp(const point &a,const point &b){//排序
double det=(a-p[1])*(b-p[1]);
if(fabs(det)>=eps)return det>0;//det>0 v在u的逆时针
return a.calc()<b.calc();
}
inline double coun(point a,point b){//计算答案
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline void graham(){
int data=1;
for(int i=2;i<=n;i++)
if(p[i].x<p[data].x||(p[i].x==p[data].x&&p[i].y<p[data].y))
data=i;
if(data!=1)swap(p[data],p[1]);
sort(p+2,p+n+1,comp);
q[++m]=p[1];//被选中的点
for(int i=2;i<=n;i++){
while(m>=2&&(q[m]-q[m-1])*(p[i]-q[m-1])<eps)m--;//判断方向
q[++m]=p[i];
}
q[m+1]=q[1];
}
inline void cal(){
double ans=0;
for(int i=1;i<=m;i++)
ans+=coun(q[i+1],q[i]);
printf("%.2lf",ans);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>p[i].x>>p[i].y;
graham();
cal();
return 0;
}