计算几何

OI中少有的以高中知识为主的版块。
1.高中计算几何基础知识
2.深刻的认识到计算几何用向量而不用解析几何。
3.图形的记录
(1):点,向量。这两个是差不多的。
(2):线:直线上一点和直线的方向向量。
(3):线段:只需要记录左右端点即可。。。。

4.正弦定理:
a S i n A = b S i n B = c S i n C = 2 R \frac {a}{Sin A} = \frac {b}{SinB}=\frac c{SinC}=2R SinAa=SinBb=SinCc=2R
R R R为三角形的外接圆半径。
5.余弦定理:
a 2 = b 2 + c 2 − 2 C o s A   b c a^2=b^2+c^2-2CosA\ bc a2=b2+c22CosA bc
C o s A = b 2 + c 2 − a 2 2 b c Cos A=\frac {b^2+c^2-a^2}{2bc} CosA=2bcb2+c2a2
6.判断点在直线的哪端
计算叉积,正为逆时针,负为逆时针。
7.C++的计算几何函数:
s i n , c o s , t a n sin , cos , tan sin,cos,tan最基础
a t a n ( x ) atan(x) atan(x) 返回的是 [ − π / 2 , + π / 2 ] [-\pi/2,+\pi/2] [π/2,+π/2]的弧度值
a t a n ( y , x ) atan(y,x) atan(y,x)返回的是(x,y)在的单位圆中以x正半轴为起始边的弧度值 [ − π , π ] [-\pi,\pi] [π,π]
a c o s ( x ) acos(x) acos(x)Principal arc cosine of x, in the interval [0,pi] radians.
a s i n ( x ) asin(x) asin(x)Principal arc sine of x, in the interval [-pi/2,+pi/2] radians.
8.基础模板

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

struct Point
{
	double x,y;
	Point(double x,double y):x(x),y(y){}
	Point operator +(const Point B)const{ return Point(x+B.x,y+B.y); }
	Point operator -(const Point B)const{ return Point(x-B.x,y-B.y); }
	Point operator *(const double B)const{ return Point(x*B,y*B); }
	double Cross(const Point B)const{ return x*B.y-y*B.x; }
	double Dot(const Point B)const{ return x*B.x+y*B.y; }
	double Len()const{ return sqrt(Dot(*this)); }
	double Angle(const Point B)const{ return acos(Dot(B)/Len()/B.Len()); }
};

int main()
{
	sin(0);
}

计算几何不要怕代码长,严谨直接不卡精度最重要。

9.快速排斥实验与跨立实验
规定「一条线段的区域」为以这条线段为对角线的,各边均与某一坐标轴平行的矩形所占的区域,那么可以发现,如果两条线段没有公共区域,则这两条线段一定不相交。
这快速排斥好像没啥用。。。。。
相反跨立实验就很有用了,线段相交的充要条件是A线段的两端点在B线段两端且B线段的两端点在A线段两端。
Code:

//线段规范相交判定
bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2)
{
    double c1 = Cross(a2 - a1, b1 - a1), c2 = Cross(a2 - a1, b2 - a1),
           c3 = Cross(b2 - b1, a1 - b1), c4 = Cross(b2 - b1, a2 - b1);
    return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

这个dcmp是用来防止卡精度的。

int dcmp(double x)
{ //三态函数,克服浮点数精度陷阱,判断x==0?x<0?x>0?
    if (fabs(x) < eps)
        return 0;
    else
        return x < 0 ? -1 : 1;
}

10:判断一点是否在任意多边形内部
(1)光线投射算法
我们考虑以该点为端点引出一条射线,如果这条射线与多边形有奇数个交点,则该点在多边形内部,否则该点在多边形外部,我们简记为奇内偶外。这个算法同样被称为奇偶规则 ,就具体实现而言,可以把射线变成长的线段,然后对每一条边做跨立实验即可。
(2)回转数算法
我们把该点与多边形的所有顶点连接起来,计算相邻两边夹角的和。注意这里的夹角是有方向的。如果夹角和为0 ,则这个点在多边形外,否则在多边形内。

11.求两条直线的交点
这个用向量也可以解决。
你们还是去看OI-WIKI
直接上公式:
直线 P ⃗ + k u ⃗ \vec{P} + k\vec{u} P +ku 与直线 Q ⃗ + k v ⃗ \vec{Q}+k\vec{v} Q +kv 的交点向量为 Q ⃗ + C r o s s ( u ⃗ , Q ⃗ − P ⃗ ) C r o s s ( u ⃗ , v ⃗ ) ∗ v ⃗ \vec{Q}+\frac {Cross(\vec{u}, \vec{Q}-\vec{P})}{Cross(\vec{u},\vec{v})}*\vec{v} Q +Cross(u ,v )Cross(u ,Q P )v
解释:(我是盗图的)在这里插入图片描述
Code:

Point GetLineIntersection(Point P, Vector v, Point Q, Vector w)
{
    Vector u = P - Q;
    double t = Cross(w, u) / Cross(v, w);
    return P + v * t;
}

有类似线代做法,简单无脑:
在这里插入图片描述
在这里插入图片描述
12 求任意多边形的面积
选一个多边形的点为原点,每个点对于这个点有一个向量,将每一条边上两个端点的向量按同一个方向求叉积和的绝对值再除二。

圆与直线相关
13.求直线与圆的交点
首先判断直线与圆的位置关系。如果直线与圆相离则无交点,若相切则可以利用切线求出切点与半径所在直线,之后转化为求两直线交点。

若有两交点,则可以利用勾股定理求出两交点的中点,然后沿直线方向加上半弦长即可。
14.求两圆交点
判断位置关系后,如果有两个交点,就用余弦定理,然后就完了。

放一个毒瘤题吧: CF 780H

15.维护可插入的凸包CF 70D
set维护极角序,插入删除点即可。

AC Code:

#include<bits/stdc++.h>
#define eps 1e-5
using namespace std;

struct Point
{
	double x,y,ag;
	Point (double x=0,double y=0):x(x),y(y){}
	Point operator +(const Point &B)const{ return Point(x+B.x,y+B.y); }
	Point operator -(const Point &B)const{ return Point(x-B.x,y-B.y); }
	double operator *(const Point &B)const{ return x*B.y-y*B.x; }
}P[3],O;

struct node
{
	bool operator ()(const Point &A,const Point &B)
	{
		return A.ag < B.ag;
	}
};
set<Point,node>st;
set<Point,node>::iterator pre,nxt,ppre,nnxt;

int main()
{
	int q,op,x,y;
	scanf("%d",&q);
	q-=3;
	scanf("%d%lf%lf",&op,&P[0].x,&P[0].y);
	scanf("%d%lf%lf",&op,&P[1].x,&P[1].y);
	scanf("%d%lf%lf",&op,&P[2].x,&P[2].y);
	O.x = (P[0].x + P[1].x + P[2].x) / 3;
	O.y = (P[0].y + P[1].y + P[2].y) / 3;
	for(int i=0;i<3;i++)
		P[i]=P[i]-O,P[i].ag=atan2(P[i].y,P[i].x);
	st.insert(P[0]),st.insert(P[1]),st.insert(P[2]);
	for(;q--;)
	{
		scanf("%d%lf%lf",&op,&P[0].x,&P[0].y);
		P[0] = P[0] - O;	
		P[0].ag = atan2(P[0].y,P[0].x);
		if(op == 1)
		{
			nxt = st.lower_bound(P[0]) , pre = nxt;
			if(pre == st.begin()) pre = st.end();
			pre --;
			if(nxt == st.end()) nxt = st.begin();
			if(((*nxt)-(*pre)) * ((*nxt)-P[0]) > eps)
			{
				for(;st.size()>=2;st.erase(*nxt),nxt=nnxt)
				{
					nnxt = nxt , nnxt ++;
					if(nnxt == st.end()) nnxt = st.begin();
					if(((*nnxt)-(*nxt)) * ((*nxt)-P[0]) < eps) break;
				}
				
				for(;st.size()>=2;st.erase(*pre),pre=ppre)
				{
					ppre = pre;
					if(ppre == st.begin()) ppre = st.end();
					ppre --;
					if((P[0] - (*pre)) * ((*pre) - (*ppre)) < eps) break;
				}
				st.insert(P[0]);
			}
		}
		else
		{
			nxt = st.lower_bound(P[0]) , pre = nxt;
			if(pre == st.begin()) pre = st.end();
			pre --;
			if(nxt == st.end()) nxt = st.begin();
			if(((*nxt)-(*pre)) * ((*nxt)-P[0]) > eps)
				puts("NO");
			else 
				puts("YES");
		}
	}
}

16.半平面交
我的模板
17.旋转卡壳
18.三维凸包
19.Pick定理、欧拉公式和圆的反演
20.最小圆覆盖
21.圆的公切线

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值