初级计算几何
前置知识
点积
点积应用:
1.判断A和B的夹角是钝角还是锐角
2.求向量A的长度
3.求向量A与B的夹角大小
叉积
叉积应用:
1.判断A与B的方向关系。
若
若
若
2.计算俩向量构成的平行四边形又向面积
3.计算三点构成的三角形的面积
4.向量旋转
5.用叉积检查俩个向量是否平行或重合
常用的平面几何公式
1.
那么
求凸包
Andrew算法
时间复杂度
把所有点以横坐标为第一关键字排序,纵坐标为第二关键字
最小的元素和最大的元素一定在凸包上
从第一个点开始遍历,如果下一个点在栈顶的两个元素所连成直线的左边,那么就入栈
否则如果在右边,说明凸包有更优的方案,上次的点出栈,
并直到新点在栈顶两个点所在的直线的左边为止
至于判断左边还是右边,用叉积
#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.半平面
一条直线和直线的一侧。半平面是一个点集,因此是一条直线和直线的一侧构成的点集。
闭半平面:包含直线
开半平面:不包含直线
一般解析式:
不理解的话,就是
2.半平面交
半平面交是指多个半平面的交集
因为半平面是点集,所以点集的交集仍然是点集
在平面直角坐标系围成一个区域
可以理解为向量集中每一个向量的右侧的交
或者
的解
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?