计算几何 - 你绝对找不到比这更好的计算几何

dzy哥哥回来给我们上课
杜老师好帅啊qwq

重新写计算几何

这些知识你需要自己推一遍

  • 线段,及点在线段上的表示(两种向量法-一个叉乘一个等和线,一种距离法)
  • 直线及其表示,用点斜法存储(学习斜率优化)
struct node3{
	double x,y,k;
}line[NNN];
  • 直线引申为半平面
  • 角度
double angle[NNN];//角度表示,用弧度制,省略单位pi
  • 多边形:按顺序(顺/逆)存点,有时候也会用有向线段(或一个表示位置的点和其他边)
  • 圆:存圆心和半径
  • 两点之间的距离
  • 面积:用叉乘就很好做
  • 小心点到直线的距离和点到线段的距离有区别
  • 判断线段是否相交,用看旋转,判两次
  • 求比例用相似导一下就行了
  • 作垂线:平行+旋转
  • 作角平分线:用对称性
  • 判断点是否在多边形内部:连的射线与边的交点奇偶(奇在内,偶在外),注意特判一些情况
  • 与圆相关的点:用交点推一下就行了

练习
POJ1269
特判就特判,不是特殊情况再求交点

POJ3907
第一道计算几何提:D
code

#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define in Read()
#define re register
const int NNN=1e6+10;
int n;
struct node{
	double x,y;
	friend inline double operator * (const node &a,const node &b){
		return a.x*b.y-a.y*b.x;
	}
}v[NNN];
double ans;

int main(){
	while(scanf("%d",&n)!=EOF&&n){
		for(re int i=1;i<=n;i++)scanf("%lf%lf",&v[i].x,&v[i].y);
		ans=0;
		for(re int i=1;i<=n;i++)ans+=v[i]*v[i%n+1];
		printf("%d\n",(int)(fabs(ans)/2));
	}
	return 0;
}

向量的运算

以前的代码好丑啊,但是我懒得改了

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
#define cross xor
#define point or
const int NNN=1e4+10;

struct node1{
	double x,y;
}dot[NNN];
struct node2{
	double x,y;
	friend inline double operator cross (const node2 &u,const node2 &v){//叉乘 
		return 1.0*u.x*v.y-1.0*u.y*v.x;
	}
	friend inline double operator point (const node2 &u,const node2 &v){//点乘
		return 1.0*u.x*v.x+1.0*u.y*v.y; 
	}
	friend inline node2 operator + (const node2 &u,const node2 &v){//向量加法 
		node2 ans;
		ans.x=u.x+v.x;
		ans.y=u.y+v.y;
		return ans;
	}
	friend inline node2 operator - (const node2 &u,const node2 &v){//向量减法 
		node2 ans;
		ans.x=u.x-v.x;
		ans.y=u.y-v.y;
		return ans;
	}
}vec[NNN];

inline node2 operator - (const node1 &a,const node1 &b){//a到b的向量(a减b)注意顺序 
	node2 ans;
	ans.x=a.x-b.x;
	ans.y=a.y-b.y;
	return ans;
}
inline node1 operator + (const node1 &a,const node2 &v){//已知起点和向量求终点
	node1 ans;
	ans.x=a.x+v.x;
	ans.y=a.y+v.y;
	return ans; 
}

int main(){
	
	return 0;
}

其他运算:

  • 向量的夹角:
    c o s < a ⃗ , b ⃗ > = a ⃗ ⋅ b ⃗ ∣ a ⃗ ∣ ∣ b ⃗ ∣ cos<\vec{a},\vec{b}>=\frac{\vec{a}·\vec{b}}{|\vec{a}||\vec{b}|} cos<a ,b >=a b a b

s i n < a ⃗ , b ⃗ > = ∣ a ⃗ × b ⃗ ∣ ∣ a ⃗ ∣ ∣ b ⃗ ∣ sin<\vec{a},\vec{b}>=\frac{|\vec{a}\times\vec{b}|}{|\vec{a}||\vec{b}|} sin<a ,b >=a b a ×b

  • 正余弦定理:自己推
  • 向量的旋转:用复平面推(用普通的旋转推也行,就是要注意一下正负)
  • 叉乘运用到的行列式:
    行列式的运算,每行任取一个不在同一列的数,相乘
    再依次排列他们的列数,其中的逆序对个数mod2就得到该积的符号(0为正,1为负)
  • 叉乘运用到右手法则
  • 向量的拐向用叉乘
  • 夹角的优劣用叉乘,夹角的钝锐用点乘(这里用到的思想就是必修四“看角的教材”里很好的思想了)

凸包

每个点(向量)要通过极角排一下序

POJ3348板子题
code

#include<bits/stdc++.h>
#define in Read()
#define re register
#define swap std::swap
#define sort std::sort
inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')f=-1,ch=getchar();
	while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=2000+10;
const double eps=1e-6;
int num;
struct node{
	double x,y;
	node(){}
	node(double _x,double _y){x=_x,y=_y;}
	friend node operator - (const node u,const node v){return node(u.x-v.x,u.y-v.y);}
	friend node operator + (const node u,const node v){return node(u.x+v.x,u.y+v.y);}
	friend node operator * (const node u,const double a){return node(u.x*a,u.y*a);}
	friend node operator / (const node u,const double a){return node(u.x/a,u.y/a);}
	friend double operator * (const node u,const node v){return (u.x*v.x+u.y*v.y);}
	friend double operator ^ (const node u,const node v){return (u.x*v.y-u.y*v.x);}
	void PRINT_VEC(){printf("%.2lf %.2lf",x,y);}
	void SCAN_VEC(){scanf("%lf%lf",&x,&y);}
}vec[NNN],convec[NNN],O;
inline double dist(node u){return sqrt(u*u);}
inline double cros(node u,node v,node w){return (u-v)^(w-v);}//u←v→w
inline bool operator < (const node u,const node v){
	double res=cros(u,vec[1],v);
	return res==0?u.x<v.x:res>0;
}

int main(){
	O.x=0,O.y=0;
	int n=in,id=1;
	for(re int i=1;i<=n;++i){
		vec[i].SCAN_VEC();
		if(vec[i].x<vec[id].x||(fabs(vec[i].x-vec[id].x)<eps&&vec[i].y<vec[id].y))id=i;
	}
	swap(vec[1],vec[id]);
	sort(vec+2,vec+n+1);
//	for(re int i=1;i<=n;++i)vec[i].PRINT_VEC(),puts("");
	convec[++num]=vec[1];
	for(re int i=2;i<=n;++i){
		while(num>1&&cros(convec[num-1],convec[num],vec[i])>0)--num;
		convec[++num]=vec[i];
	}
//	for(re int i=1;i<=num;++i)convec[i].PRINT_VEC(),puts("");
	convec[0]=convec[num];
	double res=0;
	for(re int i=1;i<=num;++i)res+=cros(convec[i-1],O,convec[i]);
	printf("%d",(int)res/100);
	
	return 0;
}

旋转卡壳

看似O(N2)实际O(N)
是不是很妙?

很久以后再看代码,发现这真是一个旋转。。。

这个代码RE了,烦请各位大佬指正

//RE码,懒得改了 
#include<cmath>
#include<cstdio>
#include<algorithm>
#define in Read()
#define re register
#define swap std::swap
#define sort std::sort
#define max std::max
inline int in{
	int i=0,f=1;char ch;
	while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
	if(ch=='-')f=-1,ch=getchar();
	while(ch<='9'&&ch>='0')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}
int n,id;
const int NNN=5000+10;
const double eps=1e-6;
int num;
struct node{
	double x,y;
	node(){}
	node(double _x,double _y){x=_x,y=_y;}
	friend node operator - (const node u,const node v){return node(u.x-v.x,u.y-v.y);}
	friend node operator + (const node u,const node v){return node(u.x+v.x,u.y+v.y);}
	friend node operator * (const node u,const double a){return node(u.x*a,u.y*a);}
	friend node operator / (const node u,const double a){return node(u.x/a,u.y/a);}
	friend double operator * (const node u,const node v){return (u.x*v.x+u.y*v.y);}
	friend double operator ^ (const node u,const node v){return (u.x*v.y-u.y*v.x);}
	void PRINT_VEC(){printf("%.2lf %.2lf",x,y);}
	void SCAN_VEC(){scanf("%lf%lf",&x,&y);}
}vec[NNN],convex[NNN],O;
inline double dist(node u){return sqrt(u*u);}
inline double cros(node u,node v,node w){return (u-v)^(w-v);}//u←v→w
inline bool operator < (const node u,const node v){
	double res=cros(u,vec[1],v);
	return res==0?u.x<v.x:res>0;
}
inline int nxt(int x){return x==n?1:x+1;}
inline int SOLVE(){
	int res=0;
	convex[n+1]=convex[1];
	for(re int i=1,j=3;i<=n;++i){
		while(nxt(j)!=i&&
		cros(convex[i],convex[i+1],convex[j])<=cros(convex[i],convex[i+1],convex[j+1]))j=nxt(j);
		res=max(res,(int)((convex[j]-convex[i])*(convex[j]-convex[i])));
		res=max(res,(int)((convex[j]-convex[i+1])*(convex[j]-convex[i+1])));
	}
	return res;
}

int main(){
	O.x=0,O.y=0;
	n=in,id=1;
	for(re int i=1;i<=n;++i){
		vec[i].SCAN_VEC();
		if(vec[i].x<vec[id].x||(fabs(vec[i].x-vec[id].x)<eps&&vec[i].y<vec[id].y))id=i;
	}
	swap(vec[1],vec[id]);
	sort(vec+2,vec+n+1);
	convex[++num]=vec[1];
	for(re int i=2;i<=n;++i){
		while(num>1&&cros(convex[num-1],convex[num],vec[i])>0)--num;
		convex[++num]=vec[i];
	}
	if(num==2){
		printf("%d",(int)((convex[1]-convex[2])*(convex[1]-convex[2])));
		return 0;
	}
	int ans=SOLVE();
	printf("%d",ans);
	return 0;
}

半平面交

处理面积交的问题
与线性规划很相似

polygon内部的所有点构成的点集叫做polygon的核
所有核的交集就是我们要求的面积

算法步骤:

  1. 极角排序,用atan2
    注意,如果你用的是逆时针围成多边形,下面要取的是向量的左侧
  2. 枚边
    能改变半平面交的就入栈
    让先前的栈内边无效的,把以前的边弹出去
目录 ㈠ 点的基本运算 1. 平面上两点之间距离 1 2. 判断两点是否重合 1 3. 矢量叉乘 1 4. 矢量点乘 2 5. 判断点是否在线段上 2 6. 求一点饶某点旋转后的坐标 2 7. 求矢量夹角 2 ㈡ 线段及直线的基本运算 1. 点与线段的关系 3 2. 求点到线段所在直线垂线的垂足 4 3. 点到线段的最近点 4 4. 点到线段所在直线的距离 4 5. 点到折线集的最近距离 4 6. 判断圆是否在多边形内 5 7. 求矢量夹角余弦 5 8. 求线段之间的夹角 5 9. 判断线段是否相交 6 10.判断线段是否相交但不交在端点处 6 11.求线段所在直线的方程 6 12.求直线的斜率 7 13.求直线的倾斜角 7 14.求点关于某直线的对称点 7 15.判断两条直线是否相交及求直线交点 7 16.判断线段是否相交,如果相交返回交点 7 ㈢ 多边形常用算法模块 1. 判断多边形是否简单多边形 8 2. 检查多边形顶点的凸凹性 9 3. 判断多边形是否凸多边形 9 4. 求多边形面积 9 5. 判断多边形顶点的排列方向,方法一 10 6. 判断多边形顶点的排列方向,方法二 10 7. 射线法判断点是否在多边形内 10 8. 判断点是否在凸多边形内 11 9. 寻点集的graham算法 12 10.寻点集凸包的卷包裹法 13 11.判断线段是否在多边形内 14 12.求简单多边形的重心 15 13.求凸多边形的重心 17 14.求肯定在给定多边形内的一个点 17 15.求从多边形外一点出发到该多边形的切线 18 16.判断多边形的核是否存在 19 ㈣ 圆的基本运算 1 .点是否在圆内 20 2 .求不共线的三点所确定的圆 21 ㈤ 矩形的基本运算 1.已知矩形三点坐标,求第4点坐标 22 ㈥ 常用算法的描述 22 ㈦ 补充 1.两圆关系: 24 2.判断圆是否在矩形内: 24 3.点到平面的距离: 25 4.点是否在直线同侧: 25 5.镜面反射线: 25 6.矩形包含: 26 7.两圆交点: 27 8.两圆公共面积: 28 9. 圆和直线关系: 29 10. 内切圆: 30 11. 求切点: 31 12. 线段的左右旋: 31 13.公式: 32
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值