板子题
题目传送门
农夫约翰想要建造一个围栏用来围住他的奶牛,可是他资金匮乏。他建造的围栏必须包括他的奶牛喜欢吃草的所有地点。对于给出的这些地点的坐标,计算最短的能够围住这些点的围栏的长度。
题目解析
显然就是求覆盖给出的点的最小凸包。
什么是凸包呢?其实就是凸多边形。显然我们发现凸包的距离是最短的。我们可以想象在一个平面上有一些钉子,现在又一根有弹性的橡皮筋要去要框住这些钉子,橡皮筋最后的形状显然就是一个最小凸包。
Graham 算法
Graham 算法的思路大致如下:
首先我们发现, \(x\) 坐标最小的点(若果有多个点的 \(x\) 坐标相等就取 \(y\) 坐标最小的)肯定是在凸包里面的。
然后其他的点按照与最初的点的极角的大小排序,极角相等则将距离小的点放在前面。
最后遍历每一个点,维护一个栈,栈里面是在凸包上的点。如果新进入的点导致图形不是凸包,那么就 退栈 来维护凸包。
代码:
#include<cmath>
#include<cstdio>
#include<algorithm>
#define I inline
#define db long double
#define U unsigned
#define R register
#define ll long long
#define RI register int
#define ull unsigned long long
#define abs(x) ((x)>0?(x):(-(x)))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define Me(a,b) memset(a,b,sizeof(a))
#define EPS (1e-7)
#define INF (0x7fffffff)
#define LL_INF (0x7fffffffffffffff)
#define maxn 100039
//#define debug
using namespace std;
#define Type int
I Type read(){
Type sum=0; int flag=0; char c=getchar();
while((c<'0'||c>'9')&&c!='-') c=getchar(); if(c=='-') c=getchar(),flag=1;
while('0'<=c&&c<='9'){ sum=(sum<<1)+(sum<<3)+(c^48); c=getchar(); }
if(flag) return -sum; return sum;
}
struct Vector{
db x,y;
Vector operator + (const Vector &x) const{ return (Vector){this->x+x.x,this->y+x.y}; }
Vector operator - (const Vector &x) const{ return (Vector){this->x-x.x,this->y-x.y}; }
double operator * (const Vector &x) const{ return this->x*x.y - this->y*x.x; }
bool operator < (const Vector &x) const { if(this->y==x.y) return this->x < x.x; return this->y < x.y; }
}a[maxn];
int n,minx;
struct Stack{
int data[maxn],_top;
int top(){ return data[_top]; }
void push(int x){ data[++_top]=x; return; }
void pop(){ _top--; return; }
void clear(){ _top=0; return; }
}s;
I db dis(Vector x,Vector y){ return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y)); }
I void swap(Vector &a,Vector &b){ Vector tmp=a; a=b; b=tmp; return; }
I Vector get(Vector x,Vector y){ return y-x; }
I int cmp(Vector x,Vector y){
if(fabs(get(a[1],x)*get(a[1],y))<EPS) return dis(a[1],x) < dis(a[1],y);
return get(a[1],x)*get(a[1],y)>0;//按照极角排序
}
int main(){
n=read(); RI i; for(i=1;i<=n;i++) scanf("%Lf%Lf",&a[i].x,&a[i].y);
minx=1; for(i=2;i<=n;i++) if(a[i]<a[minx]) minx=i; if(minx!=1) swap(a[1],a[minx]);
sort(a+2,a+n+1,cmp); s.clear(); s.push(1); s.push(2);
for(i=3;i<=n;i++){
while(get(a[s.data[s._top-1]],a[s.top()])*get(a[s.top()],a[i])<0 && s._top>=2) s.pop();
s.push(i); }
db ans=dis(a[1],a[s.top()]);
for(i=1;i<s._top;i++) ans+=dis(a[s.data[i]],a[s.data[i+1]]);
printf("%0.2Lf",ans); return 0;
}
当然这题不用开 long double
也能过