【学习笔记】计算几何(总)

资料

计算几何入门1

计算几何入门2

平面凸包入门

细节

叉积角计算:atan2(y,x)

nan判断:

  • 是否等于自己->不等即nan
  • isnan()

向量

叉积应用:

  • 快速判断点在向量左右 -> 求点是否在多边形内
  • 快速求解直线交点
  • 快速求解2向量构成三角形面积

凸包

二维凸包

算法A:类似斜率优化找凸包分别跑上下凸包
算法B:选定一点为基点将点极角排序,栈维护(极角排序相同的点按距离小的在前)
时间复杂度算法A略优于B,代码复杂度B优于A

旋转卡壳

求出凸包后选定一条边O(n) 找最远点,随选定边的移动最远点也跟着单调移动。
注意一定是选边不是点求对踵点!!!
卡壳 O(n) ,求凸包 O(nlogn)

半平面交

求多边形面积并 -> 实际求线性规划
极角排序后使用双端队列依次加入,注意要去重。
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;
}

最小圆覆盖

前置:知道不共线三点求圆心->三中垂线交点,将两中垂线表示为两点中点加垂直向量,问题转化为求向量交点。
image
如图,CO:OB=CE:FB=SACD:SADB=CACD:BABD
实际实现精度不高,建议暴力解方程。
 

具体流程如下:

圆 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]);
					}
				}
			}
		}
	}
}

时间复杂度 O(n),依赖手动随机化
随机函数:random_shuffle(p+1,p+1+n)

闵可夫斯基和

求闵可夫斯基和复杂度 O(n2) ,求闵可夫斯基和凸包复杂度 O(n)
凸包中每条边唯一对应原两凸包中的某条边,极角排序后一次加边即可。

自适应辛普森法

题解 P4525 【【模板】自适应辛普森法1】

建议直接阅读代码

点击查看代码
#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个。

posted @   flywatre  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示