【学习笔记】计算几何(总)
资料
细节
叉积角计算:atan2(y,x)
nan判断:
- 是否等于自己->不等即nan
- isnan()
向量
叉积应用:
- 快速判断点在向量左右 -> 求点是否在多边形内
- 快速求解直线交点
- 快速求解2向量构成三角形面积
凸包
二维凸包
算法A:类似斜率优化找凸包分别跑上下凸包
算法B:选定一点为基点将点极角排序,栈维护(极角排序相同的点按距离小的在前)
时间复杂度算法A略优于B,代码复杂度B优于A
旋转卡壳
求出凸包后选定一条边, 找最远点,随选定边的移动最远点也跟着单调移动。
注意一定是选边不是点求对踵点!!!
卡壳 ,求凸包 。
半平面交
求多边形面积并 -> 实际求线性规划
极角排序后使用双端队列依次加入,注意要去重。
P4196 [CQOI2006]凸多边形 /【模板】半平面交建议阅读代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define db double
int n,cnt,tot,top,back;
db ans;
const db eps=1e-7;
inline int rd(){
int f=1,j=0;
char w=getchar();
while(w>'9'||w<'0'){
if(w=='-')f=-1;
w=getchar();
}
while(w>='0'&&w<='9'){
j=(j<<3)+(j<<1)+w-'0';
w=getchar();
}
return f*j;
}
struct node{
db x,y;
node(){}
node(db _x,db _y){x=_x,y=_y;}
bool operator <(const node &t)const{return y<t.y||(y==t.y&&x<t.x);}
node operator -(node &t){return node(x-t.x,y-t.y);}
bool operator ==(const node &t)const{return x==t.x&&y==t.y;}
}_P,N[55],Ans[505];
db cpr(node A,node B){return A.x*B.y-A.y*B.x;}
db cpr(node A,node B,node C){return cpr(B-A,C-A);}
struct edge{
node start,end;
db angle;
edge(){}
edge(node A,node B){
start=A,end=B;
angle=atan2((B-A).y,(B-A).x);
}
bool operator <(const edge &t)const{
if(fabs(angle-t.angle)<=eps)return cpr(start,t.start,t.end)>0;
return angle<t.angle;
}
}e[510],dq[510];
db S1,S2;
node getnode(edge A,edge B){
S1=cpr(A.start,B.end,A.end);
S2=cpr(A.start,B.start,A.end);
return node((S1*B.start.x-S2*B.end.x)/(S1-S2),(S1*B.start.y-S2*B.end.y)/(S1-S2));
}
bool ch(edge A,edge B,edge C){
_P=getnode(B,C);
return cpr(_P,A.start,A.end)<0;
};
signed main(){
n=rd();
for(int i=1,m;i<=n;i++){
m=rd();
for(int j=1;j<=m;j++)scanf("%lf%lf",&N[j].x,&N[j].y);
for(int j=1;j<=m;j++)e[++cnt]=edge(N[j],N[j%m+1]);
}
sort(e+1,e+cnt+1);
tot=1;
for(int i=2;i<=cnt;i++)if(fabs(e[i].angle-e[i-1].angle)>eps)e[++tot]=e[i];
top=2,back=1;
dq[1]=e[1],dq[2]=e[2];
for(int i=3;i<=tot;i++){
while(back<top&&ch(e[i],dq[top],dq[top-1]))top--;
while(back<top&&ch(e[i],dq[back],dq[back+1]))back++;
dq[++top]=e[i];
}
while(back<top&&ch(dq[back],dq[top-1],dq[top]))top--;
while(back<top&&ch(dq[top],dq[back],dq[back+1]))back++;
for(int i=back;i<top;i++)Ans[i-back+1]=getnode(dq[i],dq[i+1]);
if(top-back>1)Ans[top-back+1]=getnode(dq[top],dq[back]);
tot=top-back+1;
for(int i=1;i<=tot;i++)ans+=cpr(Ans[i],Ans[i%tot+1]);
printf("%.3lf",fabs(ans)/2);
return 0;
}
最小圆覆盖
前置:知道不共线三点求圆心->三中垂线交点,将两中垂线表示为两点中点加垂直向量,问题转化为求向量交点。
如图,
实际实现精度不高,建议暴力解方程。
具体流程如下:
圆 C;
for(i=1 to n)
{
if(P[i] 不在 C 内)
{
C = {P[i], 0};
for(j=1 to i-1)
{
if(P[j] 不在 C 内)
{
C = {0.5*(P[i]+P[j]), 0.5*dist(P[i], P[j])};
for(k=1 to j-1)
{
if(P[k] 不在 C 内)
{
C = 外接圆(P[i], P[j], P[k]);
}
}
}
}
}
}
时间复杂度 ,依赖手动随机化
随机函数:random_shuffle(p+1,p+1+n)
闵可夫斯基和
求闵可夫斯基和复杂度 ,求闵可夫斯基和凸包复杂度 。
凸包中每条边唯一对应原两凸包中的某条边,极角排序后一次加边即可。
自适应辛普森法
建议直接阅读代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-7;
double a,b,c,d;
double f(double x){return (c*x+d)/(a*x+b);}
double simpson(double l,double r){return (r-l)*(f(l)+f(r)+4*f((l+r)/2))/6;}
double cal(double l,double r,double ansn){
double mid=(l+r)/2,a,b;
if(fabs((a=simpson(l,mid))+(b=simpson(mid,r))-ansn)<=eps*15)return a+b+(a+b-ansn)/15;
return cal(l,mid,a)+cal(mid,r,b);
}
double cal(double l,double r){return cal(l,r,simpson(l,r));}
signed main(){
double L,R;
scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&L,&R);
printf("%.6lf",cal(L,R));
return 0;
}
本质是通过多个二次函数拟合原函数
经典应用:求圆的面积并->拟合函数
例题
P4557 [JSOI2018]战争
闵可夫斯基和求出凸包,判点是否在凸包内。
P4357 [CQOI2016]K 远点对
旋转卡壳依次讨论第一大,第二大,....每次删去距离最远的两个点并将两点相互和其他点的距离加入堆,取k对点后弹出堆顶第k个。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】