题解 P2742 【[USACO5.1]圈奶牛Fencing the Cows】

· · 题解

来展示一下我的凸包模板。

我用的是水平序+栈

1 按x坐标为第一关键字,y坐标为第二关键字从小到大排序

2 从左到右枚举点,如果当前点相对于前面两个是向外的,就不断让栈顶出去。之后把点加入栈中。从而得到下凸包。

3 从右到左再做一遍。

#include<cstdio>
#include<cmath>
#include<algorithm> 
using namespace std;

#define oo 1000000
#define N 10100
double sqr(const double &x) { return x*x; }
struct point
{ double x,y;
  void read() { scanf("%lf%lf",&x,&y); }
  bool operator <(const point &i)const
  {
       return x<i.x||x==i.x&&y<i.y;
  }
  point operator -(const point &i)const
  {
      return (point){x-i.x,y-i.y};
  }
  double operator *(const point &i)const//叉积 得到当前方向是向左还是向右
  {
      return x*i.y-y*i.x;
  }
  friend double dis(const point &i,const point &j)
  {
      return sqrt(sqr(j.x-i.x)+sqr(j.y-i.y));
  }
}p[N],now;

int n,i;
point q[N];int top;
void push(const point &now)
{
   while ((now-q[top-1])*(q[top]-q[top-1])>0) --top;//当前点相对前面两个在外
   q[++top]=now; 
}
void tu()
{
        q[0]=q[top=1]=p[1];//设置哨点
        for (i=2;i<=n;++i) push(p[i]);
        for (i=n-1;i;--i) push(p[i]);//这时已有的点已经可以充当哨点了
}

double len()
{
    double ans=0;
    for (i=1;i<top;++i) ans+=dis(q[i],q[i+1]);
    return ans;
}

int main()
{
    //freopen("1.in","r",stdin);
    scanf("%d",&n);
    for (i=1;i<=n;++i) p[i].read();
    sort(p+1,p+n+1);
    tu();
    printf("%.2lf",len()); 
}