凸包(Andrew算法模板)

凸包(Andrew算法模板)


凸包

给定平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点;也就是能把将所有的点都围起来;

凸包有两种求法:Graham算法和Andrew算法,两种算法都需要对点惊醒排序,所以时间复杂度为O(nlogn)算法本身是O(n)瓶颈在于排序;

目前只学了Andrew算法;


Andrew算法(模板)

1.对所有的点进行排序,按x从小到大y从小到大的顺序

bool cmp(zobn a,zobn b){
	if(a.x==b.x)
	return a.y<b.y;
	return a.x<b.x;
}

2.先顺序枚举所有的点,求下凸包,用模拟维护当前在凸包上的的点;新点入栈前,先判断需要弹出那些旧点:只要新点处在栈顶两点构成的直线的右侧或共线(也就是三点连成的内侧角小于180度)(利用向量的叉积算夹角来判断)就弹出旧点

3.再逆序枚举所有点,求上凸包。和上面的维护方式一致;

(每个点最多入栈两次出栈两次(所有点共线的情况),所以总次数最多4n次)

struct zobn{
	double x,y;
}p[N],s[N]; // p位初始所有点的点集,s用来存放构成凸包的点
int n,top;
double iaji(zobn a,zobn b,zobn c){ // 已知三点求叉积
	return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
bool cmp(zobn a,zobn b){ // 自定义排序
	if(a.x==b.x)
	return a.y<b.y;
	return a.x<b.x;
}
double tubc(){
	sort(p+1,p+1+n,cmp); // 先排序
	for(int i=1;i<=n;i++){ // 顺序求下凸包
		while(top>1&&iaji(s[top-1],s[top],p[i])<=0) // 通过叉积判断是否符合
		top--;// 不符合就弹出
		s[++top]=p[i];
	}
	int t=top; // t为构成下凸包的点的个数
	for(int i=n-1;i>=1;i--){ // 求上凸包
		while(top>t&&iaji(s[top-1],s[top],p[i])<=0) // top>t防止把已经构成下凸包的点弹出去
		top--;
		s[++top]=p[i];
	}
	double an=0;
    //根据题目要求来操作求答案
	return an;
}

小试牛刀(信用卡凸包)

题目原文

[P3829 SHOI2012] 信用卡凸包

基本思路

在平面坐标系上有很多卡,卡的四个角是四分之一的圆弧,元的半径已知,卡的中心位置坐标已知,求围成的凸包的周长;

通过题目不难看出是让找出凸包;通过观察样例给的那张图片不难发现外围的直线部分就是所有圆心所构成的凸包的长度;而圆弧部分刚好可以拼成一个圆;所以我们只需找到所有圆心的点构成的凸包即可;

因为信用卡会旋转,所以我们要先编写一个旋转的函数,旋转的角度为z;

zobn xrvr(zobn p,double z) {
    return {(p.x*cos(z) - p.y*sin(z)),(p.x*sin(z) + p.y*cos(z))};
}

先假设信用卡的中心在原点位置;那么初始的四个圆心的坐标为(±b/2-r,±a/2-r);求得四个点坐标后再将信用卡平移,将点存入p数组中

double x,y,z;
cin>>x>>y>>z;
for(int i=0;i<4;i++){
	double tx=dx[i]*b;		
    double ty=dy[i]*a;
	zobn t=xrvr({tx,ty},z); 		
    p[++c]={x+t.x,y+t.y};
}

最后套用模板,再编写求距离的函数,将距离累加在加上圆弧几位所求;

代码实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N=1e5+5;
const double pi=3.1415926535; 
struct zobn{
	double x,y;
}p[N],s[N]; 
int n,top;
double iaji(zobn a,zobn b,zobn c){
	return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
bool cmp(zobn a,zobn b){
	if(a.x==b.x)
	return a.y<b.y;
	return a.x<b.x;
}
double juli(zobn a,zobn b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
} 
zobn xrvr(zobn p,double z) {
    return {(p.x*cos(z) - p.y*sin(z)),(p.x*sin(z) + p.y*cos(z))};
}
double tubc(){
	sort(p+1,p+1+n,cmp);
	for(int i=1;i<=n;i++){
		while(top>1&&iaji(s[top-1],s[top],p[i])<=0)
		top--;
		s[++top]=p[i];
	}
	int t=top;
	for(int i=n-1;i>=1;i--){
		while(top>t&&iaji(s[top-1],s[top],p[i])<=0)
		top--;
		s[++top]=p[i];
	}
	double an=0;
	for(int i=1;i<top;i++)
	an+=juli(s[i],s[i+1]);
	return an;
}
int dx[]={1,1,-1,-1};
int dy[]={1,-1,-1,1}; 
void slove(){
	cin>>n;
	double a,b,r;
	cin>>a>>b>>r;
	a=a/2-r;
	b=b/2-r;
	int c=0;
	while(n--){
		double x,y,z;
		cin>>x>>y>>z;
		for(int i=0;i<4;i++){
			double tx=dx[i]*b;
			double ty=dy[i]*a;
			zobn t=xrvr({tx,ty},z); 
			p[++c]={x+t.x,y+t.y};
		}
	}
	n=c;
	printf("%.2lf",tubc()+2*pi*r);
} 
signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int _=1;
	//cin>>_;
	while(_--)
	slove();
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值