计算几何模板合集

参考了神仙的博客

基本图形之间的操作:

view code
namespace CG{
	//--------------------准备工作-------------------- 
	const double eps=1e-6; 
	const double pi=acos(-1.0);
	struct pt{
		double x,y;
		pt(double _x=0,double _y=0){x=_x;y=_y;}
	};//点或向量 
	struct line{
		pt s,t;
		line(pt _s=pt(0,0),pt _t=pt(0,0)){s=_s;t=_t;}
	};//两点式直线/线段 
	inline int dcmp(double x){return x<-eps?-1:(x>eps?1:0);}
	inline double Abs(double x){return x*dcmp(x);}//绝对值 

	//-----------------向量基本运算-------------------- 
	inline double len(pt a){return a.x*a.x+a.y*a.y;}
	inline double Len(pt a){return sqrt(a.x*a.x+a.y*a.y);}//向量模长 
	inline pt operator +(pt a,pt b){return pt(a.x+b.x,a.y+b.y);}
	inline pt operator -(pt a,pt b){return pt(a.x-b.x,a.y-b.y);}//向量加减
	inline pt operator *(pt a,double b){return pt(a.x*b,a.y*b);}//向量数乘
	 
	inline double operator *(pt a,pt b){return a.x*b.x+a.y*b.y;}
	//点积:结果=0时a,b垂直,夹角>90度则为负数,<90度则为正数 
	inline double operator ^(pt a,pt b){return a.x*b.y-a.y*b.x;}
	//叉积:结果>0表示b在a逆时针方向,=0表示a,b共线,<0表示b在a顺时针方向
	 
	//-----------------点与向量的旋转------------------ 
	inline pt rotate(pt a,double theta){
		double x=a.x*cos(theta)+a.y*sin(theta);
		double y=-a.x*sin(theta)+a.y*cos(theta);
		return pt(x,y); 
	}//将点或向量a绕原点(向量是顶点)顺时针旋转theta的弧度 
	inline pt rotate_P(pt a,pt b,double theta){
		return rotate(a-b,theta)+b;
	}//将点a绕点b旋转theta 
	 
	//---------------点与线之间的关系------------------ 
	//命名技巧:点P(point),线段S(segment),直线L(line) 
	 
	//--------点与点-------- 
	inline bool operator ==(pt a,pt b){return (!dcmp(a.x-b.x))&&(!dcmp(a.y-b.y));}//两点重合 
	inline double dis_PP(pt a,pt b){
		return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	}//点a与点b间的距离 
	 
	//-------点与直线------- 
	inline bool judge_PL(pt a,line b){
		return !dcmp((a-b.s)^(b.t-b.s));
	}//判断点是否在直线上 
	 
	inline double dis_PL(pt a,line b){
		return Abs((a-b.s)^(a-b.t))/Len(b.t-b.s); 
	}//点与直线的距离:面积除以底边长 
	 
	inline pt Footprint(pt a,line b){
		pt x=a-b.s,y=a-b.t,z=b.t-b.s;
		double s1=(x*z)/Len(z),s2=-1.0*(y*z)/Len(z);//分别求出AS,AT关于ST的投影
		return b.s+z*(s1/(s1+s2)); 
	}//点A关于直线ST的垂足 
	 
	inline pt Symmetry_PL(pt a,line b){
		return a+(Footprint(a,b)-a)*2.0; 
	}//点a关于直线b的对称点 
	 
	//-------点与线段------- 
	inline bool judge_PS(pt a,line b){
		return (!dcmp((a-b.s)^(b.t-b.s)))&&(dcmp((a-b.s)*(a-b.t))<=0);
	}//判断点是否在线段上
	 
	inline double dis_PS(pt a,line b){
		pt x=a-b.s,y=a-b.t,z=b.t-b.s;
		if(x*z<0) return Len(x);//距离左端点最近
		if(y*z>0) return Len(y);//距离右端点最近
		return dis_PL(a,b);  
	}//点与线段的距离
	 
	inline pt point_PS(pt a,line b){
		pt x=a-b.s,y=a-b.t,z=b.t-b.s;
		if(x*z<0) return b.s;//距离左端点最近
		if(y*z>0) return b.t;//距离右端点最近
		return Footprint(a,b);  
	}//点a在线段b上距离最近的点
	 
	//------------线与线---------
	inline pt cross_LL(line a,line b){
		pt x=a.t-a.s,y=b.t-b.s,z=a.s-b.s;
		return a.s+x*((y^z)/(x^y)); 
	}  
	inline bool judge_cross_SL(line a,line b){
		return judge_PS(cross_LL(a,b),a);//看交点是否在线段上即可 
	}//判断线段a与直线b是否相交 
	 
	inline bool judge_cross_SS(line a,line b){
		double s1=(a.t-a.s)^(b.s-a.s),s2=(a.t-a.s)^(b.t-a.s);
		double s3=(b.t-b.s)^(a.s-b.s),s4=(b.t-b.s)^(a.t-b.s);
		return dcmp(s1)*dcmp(s2)<0&&dcmp(s3)*dcmp(s4)<0;//a的端点在b的两侧且b的端点在a的两侧 
	}//判断两线段是否相交 
	
	//----------多边形------------
	struct poly{
		vector<pt> pts;
		inline int include(pt a){//PIP
			int n=pts.size(),cnt=0;
			for(int i=0;i<n;++i){
				pt s=pts[i],t=i<n-1?pts[i+1]:pts[0];
				if(judge_PL(a,line(s,t))) return 2;
				if(a.y-min(s.y,t.y)>=-eps&&a.y-max(s.y,t.y)<=eps){
					double nx=s.x+(a.y-s.y)*(t.x-s.x)/(t.y-s.y); 
					if(a.x-nx<=-eps) cnt++;
				}
			}
			return cnt&1;
		}//0:在多边形外,1:在多边形内,2:在多边形的边上 
		inline double area(){
			double ans=0;
			for(int i=0;i<pts.size();++i) ans+=pts[i]^pts[i<pts.size()-1?i+1:0]; 
			return ans/2;
		}	
		inline bool Left(pt x,pt l,pt r){
			return dcmp((l-x)^(r-x))<=0; 
		}//xl是否在xr左侧 
		inline int include_bs(pt p){
			int n=pts.size();
			if(!Left(pts[0],p,pts[1])) return 0;
			if(!Left(pts[0],pts[n-1],p)) return 0;
			if(judge_PL(p,line(pts[0],pts[1]))) return 2;
			if(judge_PL(p,line(pts[0],pts[n-1]))) return 2;
			int l=1,r=n-2,ans=1;
			while(l<=r){
				int mid=(l+r)>>1;
				if(!Left(pts[0],pts[mid],p)) l=mid+1,ans=mid;
				else r=mid-1;
			}
			if(!Left(pts[ans],p,pts[ans+1])) return 0;
			if(judge_PL(p,line(pts[ans],pts[ans+1]))) return 2;
			return 1;
		}//二分法 
	};
}
using namespace CG;

凸包:

Graham算法
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const double eps=1e-8;
struct pt{
	double x,y;
	pt(double _x=0,double _y=0){x=_x;y=_y;} 
};
struct line{
	pt s,t;
	line(pt _s=pt(0,0),pt _t=pt(0,0)){s=_s;t=_t;}
}; 
inline int dcmp(double x){return x<-eps?-1:(x>eps?1:0);}
inline pt operator -(pt a,pt b){return pt(a.x-b.x,a.y-b.y);}
inline double operator ^(pt a,pt b){return a.x*b.y-a.y*b.x;}
inline double dis_PP(pt a,pt b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));} 

int n,top;
pt p[N],stk[N]; 
inline bool cmp(pt x,pt y){
	int pd=dcmp((x-p[1])^(y-p[1]));
	if(pd>0) return true;
	if(pd==0&&dis_PP(x,p[1])<dis_PP(y,p[1])) return true;
	return false; 
}
inline double Graham(){
	for(int i=2;i<=n;++i) if(p[i].y<p[1].y||(p[i].y==p[1].y&&p[i].x<p[1].x)) swap(p[1],p[i]);
	stk[++top]=p[1];
	sort(p+2,p+n+1,cmp);
	for(int i=2;i<=n;++i){
		while(top>1){
			if(dcmp((stk[top]-stk[top-1])^(p[i]-stk[top]))<0) --top;
			else if(dcmp((stk[top]-stk[top-1])^(p[i]-stk[top]))==0&&dis_PP(stk[top-1],stk[top])>dis_PP(stk[top-1],p[i])) --top;
			else break;
		}
		stk[++top]=p[i];
	}
	stk[++top]=p[1];
	double ans=0;
	for(int i=2;i<=top;++i) ans+=dis_PP(stk[i-1],stk[i]);
	return ans; 
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%lf%lf",&p[i].x,&p[i].y);
	printf("%.2lf\n",Graham());
	return 0;
}
Andrew算法
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const double eps=1e-8;
struct pt{
	double x,y;
	pt(double _x=0,double _y=0){x=_x;y=_y;}
};
inline int dcmp(double x){return (x>eps)?1:(x<-eps?-1:0);}
inline pt operator -(pt a,pt b){return pt(a.x-b.x,a.y-b.y);}
inline double operator ^(pt a,pt b){return a.x*b.y-a.y*b.x;}
inline bool cmp(pt a,pt b){return (a.x!=b.x)?a.x<b.x:a.y<b.y;}
inline double dis_PP(pt a,pt b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}

inline int Andrew(pt *p,pt *q,int n){
​	sort(p+1,p+n+1,cmp);
​	int top=0;
​	q[++top]=p[1];
​	for(int i=2;i<=n;++i){
​		while(top>=2&&((q[top-1]-q[top])^(p[i]-q[top]))>=0) top--;
​		q[++top]=p[i];
​	}
​	int now=top;
​	for(int i=n-1;i>=1;--i){
​		while(top>now&&((q[top-1]-q[top])^(p[i]-q[top]))>=0) top--;
​		q[++top]=p[i];
​	}
​	if(n>1) --top;
​	return top;
}

int n;
pt p[N],q[N];
int main(){
​	scanf("%d",&n);
​	for(int i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y);
​	int tot=Andrew(p,q,n);
​	double ans=0;
​	for(int i=2;i<=tot;++i) ans+=dis_PP(q[i],q[i-1]);ans+=dis_PP(q[1],q[tot]);
​	printf("%.2lf\n",ans);
​	return 0;
}
旋转卡壳求凸包直径
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct pt{
	int x,y;
	pt(int _x=0,int _y=0){x=_x;y=_y;}
};
inline pt operator -(pt a,pt b){return pt(a.x-b.x,a.y-b.y);}
inline int len(pt a){return a.x*a.x+a.y*a.y;}
inline int operator ^(pt a,pt b){return a.x*b.y-a.y*b.x;}
inline bool cmp(pt a,pt b){return (a.x!=b.x)?a.x<b.x:a.y<b.y;}
inline int Andrew(pt *p,pt *q,int n){
	int top=0;
	sort(p+1,p+n+1,cmp);
	q[++top]=p[1];
	for(int i=2;i<=n;++i){
		while(top>=2&&((q[top-1]-q[top])^(p[i]-q[top]))>=0) --top;
		q[++top]=p[i];
	}
	int now=top;
	for(int i=n-1;i>=1;--i){
		while(top>now&&((q[top-1]-q[top])^(p[i]-q[top]))>=0) --top;
		q[++top]=p[i];
	}
	if(n>1) --top;
	return top;
}
inline int S(pt x,pt y,pt z){return abs((z-x)^(y-x));}
inline int nxt(int i,int n){return i<n?i+1:1;}
inline int diameter(pt *p,int n){
	int ans=0;
	for(int i=1,j=2;i<=n;++i){
		for(;;j=nxt(j,n)){
			if(S(p[nxt(j,n)],p[i],p[nxt(i,n)])<=S(p[j],p[i],p[nxt(i,n)])){
				ans=max(ans,len(p[j]-p[i]));
				ans=max(ans,len(p[j]-p[nxt(i,n)]));
				break;
			}
		}
	}
	return ans;
}
int n; 
pt p[N],q[N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y);
	int tot=Andrew(p,q,n);
	printf("%d\n",diameter(q,tot));
	return 0;
}
半平面交求凸多边形交的面积
#include<bits/stdc++.h>
using namespace std;
const int N=510;
const double eps=1e-8;
struct pt{
	double x,y;
	pt(double _x=0,double _y=0){x=_x;y=_y;}
};
struct line{
	pt s,t;
	line(pt _s=pt(0,0),pt _t=pt(0,0)){s=_s;t=_t;}
}q[N];

int n,m,tot;
inline double angle(const pt &p){return atan2(p.y,p.x);}
inline int dcmp(const double &x){return (x>eps)?1:(x<-eps?-1:0);}
inline pt operator +(const pt &a,const pt &b){return pt(a.x+b.x,a.y+b.y);}
inline pt operator -(const pt &a,const pt &b){return pt(a.x-b.x,a.y-b.y);}
inline double operator ^(const pt &a,const pt &b){return a.x*b.y-a.y*b.x;}
inline pt operator *(const pt &a,const double &x){return pt(a.x*x,a.y*x);}
inline pt operator /(const pt &a,const double &x){return pt(a.x/x,a.y/x);}

inline bool cmp(const line &a,const line &b){
​	double a1=angle(a.t-a.s),a2=angle(b.t-b.s); 
​	if(dcmp(a2-a1)!=0) return a1<a2;
​	return dcmp((b.s-a.s)^(b.t-a.s))>0; 
}
line li[N]; 
pt p[N];//p[i]是li[i]与li[i-1]的交点 
inline pt cross_LL(const line &a,const line &b){
​	double s1=(a.s-b.t)^(a.t-b.t),s2=(a.t-b.s)^(a.s-b.s);
​	return b.s+(b.t-b.s)/(s1+s2)*s2;
}
inline int SI(){
​	sort(q+1,q+tot+1,cmp);
​	int len=0;
​	for(int i=1;i<=tot;++i){
​		if(i>1&&!dcmp(angle(q[i].t-q[i].s)-angle(q[i-1].t-q[i-1].s))) continue;
​		q[++len]=q[i];
​	}
​	int l=1,r=0;
​	for(int i=1;i<=len;++i){
​		while(l<r&&dcmp((q[i].t-p[r])^(q[i].s-p[r]))>0) r--;
​		while(l<r&&dcmp((q[i].t-p[l+1])^(q[i].s-p[l+1]))>0) l++; 
​		li[++r]=q[i];if(r>l) p[r]=cross_LL(li[r],li[r-1]);
​	}
​	while(l<r&&dcmp((q[l].t-p[r])^(q[l].s-p[r]))>0) r--;
​	p[r+1]=cross_LL(li[r],li[l]);r++;
​	len=r-l;
​	for(int i=1,j=l+1;j<=r;++i,++j) p[i]=p[j];
​	return len;
}
inline double area(int n){
​	double ans=0;
​	for(int i=2;i<n;++i) ans+=(p[i+1]-p[1])^(p[i]-p[1]);
​	return fabs(ans)/2;
}
int main(){
​	scanf("%d",&n);
​	while(n--){
​		scanf("%d",&m);
​		pt last,st,now;
​		for(int i=1;i<=m;++i){
​			scanf("%lf%lf",&now.x,&now.y);
​			if(i>1) q[++tot]=line(last,now);
​			else st=now;
​			last=now; 
​		}
​		q[++tot]=line(last,st);
​	}
​	printf("%.3lf\n",area(SI()));
​	return 0;
}
上一篇:MySQL 主从同步(3)-percona-toolkit工具(数据一致性监测、延迟监控)使用梳理


下一篇:[刷题之旅no35]逆序对