(模板)[USACO5.1]圈奶牛Fencing the Cows (二维凸包)
题目链接:https://www.luogu.com.cn/problem/P2742
求凸包有两种常用算法:
Graham算法:
首先将所有点按纵坐标从小到大排序,纵坐标相同则按横坐标从小到大排序,
排序后的第一个点一定位于凸包上,以第一个点为原点进行极角排序,
然后维护一个单调栈,如果新进来的点与栈顶两个元素形成凹壳则弹出栈顶,一直进行上述操作直到形成凸壳,
最后栈中的点即为凸包上的点
时间复杂度 \(O(nlogn)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000;
const double eps = 1e-8;
int n;
double ans = 0;
struct Point{
double x, y;
}a[maxn];
Point operator + (Point a, Point b){ return (Point){a.x + b.x, a.y + b.y}; }
Point operator - (Point a, Point b){ return (Point){a.x - b.x, a.y - b.y}; }
Point operator * (Point a, double b){ return (Point){a.x * b, a.y * b}; }
Point operator / (Point a, double b){ return (Point){a.x / b, a.y / b}; }
double cross(Point o, Point a, Point b){ return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y); }
double dis(Point a, Point b){ return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }
bool cmpy(Point a, Point b){
if(fabs(a.y - b.y) < eps) return a.x < b.x;
return a.y < b.y;
}
bool cmp_angle(Point b, Point c){
if(fabs(cross(a[1], b, c)) < eps) return dis(a[1], b) < dis(a[1], c) + eps;
if(cross(a[1], b, c) < 0) return 0;
else return 1;
}
Point sta[maxn]; int top = 0;
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
scanf("%d", &n);
for(int i = 1 ; i <= n ; ++i){ scanf("%lf%lf", &a[i].x, &a[i].y); }
sort(a + 1, a + 1 + n, cmpy);
sort(a + 2, a + 1 + n, cmp_angle);
sta[++top] = a[1];
sta[++top] = a[2];
for(int i = 3 ; i <= n ; ++i){
while(top > 1 && cross(sta[top - 1], a[i], sta[top]) > eps) --top;
sta[++top] = a[i];
}
for(int i = 2 ; i <= top ; ++i){
ans += dis(sta[i], sta[i - 1]);
}
ans += dis(sta[1], sta[top]);
printf("%.2lf\n", ans);
return 0;
}