(模板)[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;
}
posted @ 2021-01-13 22:29  Tartarus_li  阅读(76)  评论(0编辑  收藏  举报