初级计算几何

前置知识

点积

x1x2+y1y2
点积应用:
1.判断A和B的夹角是钝角还是锐角
2.求向量A的长度
3.求向量A与B的夹角大小

叉积

x1y2y1x2
叉积应用:
1.判断A与B的方向关系。
A×B>0,BA 的逆时针方向
A×B<0,BA 的顺时针方向
A×B=0,BA 共线,可能是同方向的,也可能是反方向的
2.计算俩向量构成的平行四边形又向面积
3.计算三点构成的三角形的面积
4.向量旋转
5.用叉积检查俩个向量是否平行或重合

常用的平面几何公式

1.y1=k1x+b1 , y2=k2x+b2求交点(x,y)
那么x=(b2b1)/(k1k2),y自行代入计算

求凸包

Andrew算法

时间复杂度O(nlogn)
把所有点以横坐标为第一关键字排序,纵坐标为第二关键字
最小的元素和最大的元素一定在凸包上
从第一个点开始遍历,如果下一个点在栈顶的两个元素所连成直线的左边,那么就入栈
否则如果在右边,说明凸包有更优的方案,上次的点出栈,
并直到新点在栈顶两个点所在的直线的左边为止
至于判断左边还是右边,用叉积

#include <bits/stdc++.h>
using namespace std;

const int N=2e5+100;

struct cor {

	double x,y;

}a[N],b[N];

double dist(cor p,cor q) {
	double A=p.x-q.x,B=p.y-q.y;
	return sqrt(A*A+B*B);
}

double cross(cor p,cor r,cor q) {
	double x1=p.x-r.x,y1=p.y-r.y;
	double x2=q.x-r.x,y2=q.y-r.y;
	return (x1*y2)-(x2*y1);
}

bool cmp(cor p,cor q) {
	if(p.x==q.x) return p.y<q.y;
	return p.x<q.x;
}

int n,cnt=0;
double ans=0; 
void Andrew() {
	
	sort(a+1,a+n+1,cmp);
	b[++cnt]=a[1];
	for(int i=2;i<=n;i++) {
		
		while(cnt>1&&cross(b[cnt],b[cnt-1],a[i])<=0) cnt--;
		b[++cnt]=a[i];
		
	}
	int t=cnt;
	for(int i=n-1;i>=1;i--) {
		
		while(cnt>t&&cross(b[cnt],b[cnt-1],a[i])<=0) cnt--;
		b[++cnt]=a[i];
		
	}
	
}

int main() {
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%lf%lf",&a[i].x,&a[i].y);
	}
	Andrew();
	for(int i=1;i<cnt;i++) ans+=dist(b[i],b[i+1]);
	ans+=dist(b[cnt],b[1]);
	printf("%.2lf\n",ans);

	return 0;
}

旋转卡壳

先求出凸包,然后枚举每一条边,去找对踵点,找出凸包直径
我们可以把点到直线的距离化解为三角形的面积,再运用叉积进行比较
凸包上的点依次与对应边产生的距离成单峰函数,面积上升到最高点后,又会下降
记录并维护一个当前最远点,并不断计算、更新答案即可
凸包的精髓就在于在它上面的各种单峰函数性质
通常会用单调栈来维护单调性,这里常常是解题的突破口

#include <bits/stdc++.h>
using namespace std;

const int N=2e5+100;

struct cor {

	double x,y;

} a[N],b[N];

double dist(cor p,cor q) {
	double A=p.x-q.x,B=p.y-q.y;
	return sqrt(A*A+B*B);
}

double cross(cor p,cor r,cor q) {
	double x1=p.x-r.x,y1=p.y-r.y;
	double x2=q.x-r.x,y2=q.y-r.y;
	return (x1*y2)-(x2*y1);
}

bool cmp(cor p,cor q) {
	if(p.x==q.x) return p.y<q.y;
	return p.x<q.x;
}

int n,cnt=0;
double ans=0;
void Andrew() {

	sort(a+1,a+n+1,cmp);
	b[++cnt]=a[1];
	for(int i=2; i<=n; i++) {

		while(cnt>1&&cross(b[cnt],b[cnt-1],a[i])<=0) cnt--;
		b[++cnt]=a[i];

	}
	int t=cnt;
	for(int i=n-1; i>=1; i--) {

		while(cnt>t&&cross(b[cnt],b[cnt-1],a[i])<=0) cnt--;
		b[++cnt]=a[i];

	}

}

signed main() {
	
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		scanf("%lf%lf",&a[i].x,&a[i].y);
	}
	Andrew();
	b[cnt+1]=b[1];
	int j=2;
	for(int i=1;i<=cnt;i++) {
		
		while(fabs(cross(b[i],b[j],b[i+1]))<fabs(cross(b[i],b[j+1],b[i+1]))) {
			j++;
			if(j>=cnt) j=1;
		}
		ans=max(ans,max(dist(b[i],b[j]),dist(b[i+1],b[j])));
		
	}
	ans=ans*ans;
	cout<<(int)ans;

	return 0;
}

半平面交

定义

1.半平面
一条直线和直线的一侧。半平面是一个点集,因此是一条直线和直线的一侧构成的点集。
闭半平面:包含直线
开半平面:不包含直线
一般解析式:Ax+By+C>=0
不理解的话,就是 y=abxcb=0 周围的一个半平面,具体部位要分类讨论
2.半平面交
半平面交是指多个半平面的交集
因为半平面是点集,所以点集的交集仍然是点集
在平面直角坐标系围成一个区域
可以理解为向量集中每一个向量的右侧的交
或者
A1x+B1y+C>=0
A2x+B2y+C>=0
......

的解

code

#include <bits/stdc++.h>
using namespace std;

const int N=5e3+100;
const double eps=1e-7;

struct cor {

	double x,y;

} a[N],ans[N];

struct edge {

	cor st,en;
	double K;
	void add(cor x,cor y) {

		st.x=x.x,en.x=y.x;
		st.y=x.y,en.y=y.y;
		K=atan2(y.y-x.y,y.x-x.x);//斜率

	}

} s[N],b[N];

double cross(cor p,cor r,cor q) {

	double x1=p.x-r.x,x2=q.x-r.x;
	double y1=p.y-r.y,y2=q.y-r.y;
	return (x1*y2-y1*x2);

}

bool ok(cor x,edge y) {

	if(cross(x,y.st,y.en)<0) return true;
	else return false;

}

bool cmp(edge x,edge y) {

	if(x.K<y.K) return true;
	if(fabs(x.K-y.K)<eps&&ok(x.st,y)==true) return true;
	return false;

}

cor pot(edge p,edge q) {

	cor s1=p.st,s2=q.st;
	cor e1=p.en,e2=q.en;
	double a1=s1.y-e1.y;
	double b1=e1.x-s1.x;
	double c1=s1.x*e1.y-s1.y*e1.x;
	double a2=s2.y-e2.y;
	double b2=e2.x-s2.x;
	double c2=s2.x*e2.y-s2.y*e2.x;
	cor d;
	d.x=(c1*b2-c2*b1)/(a2*b1-a1*b2);
	d.y=(c2*a1-c1*a2)/(a2*b1-a1*b2);
	return d;

}

bool check(edge A,edge B,edge C) {

	cor P=pot(B,C);
	return cross(A.st,P,A.en)<0;

}

int n,cnt=0,m,top=0;

int main() {

	scanf("%d",&n);
	for(int j=1; j<=n; j++) {
		scanf("%d",&m);
		for(int i=1; i<=m; i++)
			cin>>a[i].x>>a[i].y;
		for(int i=1; i<=m; i++) {

			cor x=a[i],y=a[i%m+1];
			s[++cnt].add(x,y);

		}

	}

	sort(s+1,s+cnt+1,cmp);
	int tot=1;
	for(int i=2; i<=cnt; i++) {
		if(fabs(s[i].K-s[i-1].K)>eps) s[++tot]=s[i];
	}

	b[1]=s[1],b[2]=s[2];
	int l=1,r=2;
	for(int i=3; i<=tot; i++) {

		while(l<r&&check(s[i],b[r],b[r-1])) r--;
		while(l<r&&check(s[i],b[l],b[l+1])) l++;
		b[++r]=s[i];

	}

	while(l<r&&check(s[l],b[r],b[r-1])) r--;
	while(l<r&&check(s[r],b[l],b[l+1])) l++;

	for(int i=l; i<r; i++) ans[i-l+1]=pot(b[i],b[i+1]);
	if(r-l>1) ans[r-l+1]=pot(b[l],b[r]);

	int num=r-l+1;
	double ANS=0;
	for(int i=3; i<=num; i++) ANS+=cross(ans[i],ans[1],ans[i-1]);
	printf("%.3lf\n",(fabs(ANS)/2));

	return 0;
}
posted @   Diamondan  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示