计算几何

目录

注意事项

基本运算

判断正负函数(sgn)

点积(数量积、内积)(Dot)

向量积,叉积(Cross)

判断向量bc是不是向ab的逆时针方向(左边)转(ToLeftTest)

取模(模长,求长度)(Length)

计算两向量夹角(Angle)

计算两向量构成的平行四边形有向面积(Area2)

代码模板

代码模板2

点与线

判断点和直线关系(relation)

计算点 P 到直线 AB 的垂足

判断点是否在线段上(OnSegment)

多边形

三角形

三角形四心

三角形面积

四边形

正n边形

扇形面积

求两圆相交面积(AreaOfOverlap)

圆锥

求多边形的重心

求多边形面积

凸包

Graham Scan

Andrew算法

Minimal Circle最小圆覆盖问题

A Triangle and a Circle三角形与圆相交求面积

area多边形与圆相交求面积

离散化

区域的个数

一些函数/定理/应用

Pick定理(根据点在多边形内和边界的个数求面积)

判断四点共面(混合积)


注意事项

计算误差

尽量少用三角函数、除法、开方、求幂、取对数运算

如:1.0 / 2.0 ∗ 3.0 / 4.0 ∗ 5.0 = ( 1.0 ∗ 3.0 ∗ 5.0 ) / ( 2.0 ∗ 4.0 )

在不溢出的情况下将除式比较转化为乘式比较

误差判别法

const double eps = 1e-9;
int dcmp(double x, double y) {
	if(fabs(x - y) < eps)
		return 0;
	if(x > y)
		return 1;
	return -1;
}

化浮为整

在不溢出整数范围的情况下,可以通过乘上10^k转化为整数运算,最后再将结果转化为浮点数输出

注意负零

double a = -0.000001;
printf("%.4f", a);

注意反三角函数的值域

double x = 1.000001;
double acx = acos(x);//可能会返回runtime error(cos值域是-1到1)
//此时我们可以加一句判断
double x = 1.000001;
if(fabs(x - 1.0) < eps || fabs(x + 1.0) < eps)//如果x等于+1/-1就四舍五入
    x = round(x);
double acx = acos(x);

计算几何常用开头模板

const double pi = acos(-1.0);
const double eps = 1e-8;//一般出题人比较喜欢用这个数
double x = 1.49999;
    int fx = floor(x);//向下取整函数
    int cx = ceil(x);//向上取整函数
    int rx = round(x);//四舍五入函数
    printf("%f %d %d %d\n", x, fx, cx, rx);
    //输出结果 1.499990 1 2 1

基本运算

判断正负函数(sgn)

//sgn三态函数用于减少浮点的精度问题
int sgn(double x) {
	if(fabs(x) < eps)
		return 0;
	if(x < 0)
		return -1;
	return 1;
}
//重载等于运算符
bool operator == (const Point& a, const Point& b) {
	return !sgn(a.x - b.x) && !sgn(a.y - b.y);
}

点积(数量积、内积)(Dot)

//点积(满足交换律)
double Dot(Vector A, Vector B) {
	return A.x * B.x + A.y * B.y;
}

向量积,叉积(Cross)

几何意义

向量α与β所张成的平行四边形的有向面积

判断外积的符号 --右手定则

α × β 

若β在α的逆时针方向,则为正值、顺时针则为负值、两向量共线则为0

//向量的叉积(不满足交换律)

//等于两向量有向面积的二倍

//(从v的方向看,w在左边,叉积>0,w在右边,叉积<0,共线,叉积=0)

//cross(x, y) = -cross(y, x)
//cross(x, y) : xAyB - xByA
double Cross(Vector A, Vector B) {
	return A.x *  B.y - B.x * A.y;
}

判断向量bc是不是向ab的逆时针方向(左边)转(ToLeftTest)

也可以看作一个点c是否在向量ab的左边

凸包构造时将会频繁用到此公式

bool ToLeftTest(Point a, Point b, Point c) {
	return Cross(b - a, c - b) > 0;
}

取模(模长,求长度)(Length)

double Length(Vector A) {
	return sqrt(Dot(A, A));
}

计算两向量夹角(Angle)

返回值为弧度制下的夹角

double Angle(Vector A, Vector B) {
	return acos(Dot(A, B) / Length(A) / Length(B));
}

计算两向量构成的平行四边形有向面积(Area2)

//三个点确定两个向量,(交点为A的两个向量AB和AC)

//然后求这两个向量的叉积(叉乘)

double Area2(Point A, Point B, Point C) {
	return Cross(B - A, C - A);
}

代码模板

struct Point {
	double x,y;
	Point() {}
	Point(double _x,double _y) {
		x = _x;
		y = _y;
	}
	void input() {
		scanf("%lf%lf",&x,&y);
	}
	void output() {
		printf("%.2f %.2f\n",x,y);
	}
	bool operator == (Point b)const {
		return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
	}
	bool operator < (Point b)const {
		return sgn(x-b.x) == 0 ? sgn(y-b.y)<0 : sgn(x-b.x)<0;
	}
	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;
	}
	// 点积
	double operator * (const Point &b)const {
		return x*b.x + y*b.y;
	}
	// 返回长度
	double len() {
		return hypot(x,y); // 库函数
	}
	//
	double len2() {
		return x*x + y*y;
	}
	double distance(Point p) {
		return hypot(x-p.x,y-p.y);
	}
	Point operator + (const Point &b)const {
		return Point(x+b.x,y+b.y);
	}
	Point operator * (const double &k)const {
		return Point(x*k,y*k);
	}
	Point operator / (const double &k)const {
		return Point(x/k,y/k);
	}
	// 计算 pa 和 pb 的夹角
	double rad(Point a,Point b) {
		Point p = *this;
		return fabs(atan2(fabs((a-p)^(b-p)),(a-p)*(b-p)));
	}
	// 化为长度为r的向量
	Point trunc(double r) {
		double l = len();
		if(!sgn(l)) return *this;
		r /= l;
		return Point(x*r,y*r);
	}
	// 逆时针旋转90度(绕原点)
	Point rotleft() {
		return Point(-y,x);
	}
	// 顺时针旋转90度(绕原点)
	Point rotright() {
		return Point(y,-x);
	}
	// 绕着p点逆时针旋转angle
//已知点A(x,y),绕点P(a,b)逆时针转 θ得到B(x1,y1)
//x1=a+x*cos( θ)-y*sin( θ)
//y1=b+x*sin( θ)+y*cos( θ)
	Point rotate(Point p,double angle) {
		Point v = (*this) - p;
		double c = cos(angle),s = sin(angle);
		return Point(p.x + v.x*c - v.y*s,p.y+v.x*s + v.y*c);
	}
};

代码模板2

#define eps 1e-8
#define zero(x) (((x)>0?(x):-(x))<eps)
struct point {
	double x,y;
};
struct line {
	point a,b;
};
//计算cross product (P1-P0)x(P2-P0)
double xmult(point p1,point p2,point p0) {
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
double xmult(double x1,double y1,double x2,double y2,double x0,double y0) {
	return (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
}
//计算dot product (P1-P0).(P2-P0)
double dmult(point p1,point p2,point p0) {
	return (p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y);
}
double dmult(double x1,double y1,double x2,double y2,double x0,double y0) {
	return (x1-x0)*(x2-x0)+(y1-y0)*(y2-y0);
}
//两点距离
double distance(point p1,point p2) {
	return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
double distance(double x1,double y1,double x2,double y2) {
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
//判三点共线
int dots_inline(point p1,point p2,point p3) {
	return zero(xmult(p1,p2,p3));
}
int dots_inline(double x1,double y1,double x2,double y2,double x3,double y3) {
	return zero(xmult(x1,y1,x2,y2,x3,y3));
}
//判点是否在线段上,包括端点
int dot_online_in(point p,line l) {
	return zero(xmult(p,l.a,l.b))&&(l.a.x-p.x)*(l.b.x-p.x)<eps&&(l.a.y-p.y)*(l.b.y-p.y)<eps;
}
int dot_online_in(point p,point l1,point l2) {
	return zero(xmult(p,l1,l2))&&(l1.x-p.x)*(l2.x-p.x)<eps&&(l1.y-p.y)*(l2.y-p.y)<eps;
}
int dot_online_in(double x,double y,double x1,double y1,double x2,double y2) {
	return zero(xmult(x,y,x1,y1,x2,y2))&&(x1-x)*(x2-x)<eps&&(y1-y)*(y2-y)<eps;
}
//判点是否在线段上,不包括端点
int dot_online_ex(point p,line l) {
	return dot_online_in(p,l)&&(!zero(p.x-l.a.x)||!zero(p.y-l.a.y))&&(!zero(p.x-l.b.x)||!zero(p.y-l.b.y));
}
int dot_online_ex(point p,point l1,point l2) {
	return dot_online_in(p,l1,l2)&&(!zero(p.x-l1.x)||!zero(p.y-l1.y))&&(!zero(p.x-l2.x)||!zero(p.y-l2.y));
}
int dot_online_ex(double x,double y,double x1,double y1,double x2,double y2) {
	return dot_online_in(x,y,x1,y1,x2,y2)&&(!zero(x-x1)||!zero(y-y1))&&(!zero(x-x2)||!zero(y-y2));
}
//判两点在线段同侧,点在线段上返回0
int same_side(point p1,point p2,line l) {
	return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)>eps;
}
int same_side(point p1,point p2,point l1,point l2) {
	return xmult(l1,p1,l2)*xmult(l1,p2,l2)>eps;
}
//判两点在线段异侧,点在线段上返回0
int opposite_side(point p1,point p2,line l) {
	return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)<-eps;
}
int opposite_side(point p1,point p2,point l1,point l2) {
	return xmult(l1,p1,l2)*xmult(l1,p2,l2)<-eps;
}
//判两直线平行
int parallel(line u,line v) {
	return zero((u.a.x-u.b.x)*(v.a.y-v.b.y)-(v.a.x-v.b.x)*(u.a.y-u.b.y));
}
int parallel(point u1,point u2,point v1,point v2) {
	return zero((u1.x-u2.x)*(v1.y-v2.y)-(v1.x-v2.x)*(u1.y-u2.y));
}
//判两直线垂直
int perpendicular(line u,line v) {
	return zero((u.a.x-u.b.x)*(v.a.x-v.b.x)+(u.a.y-u.b.y)*(v.a.y-v.b.y));
}
int perpendicular(point u1,point u2,point v1,point v2) {
	return zero((u1.x-u2.x)*(v1.x-v2.x)+(u1.y-u2.y)*(v1.y-v2.y));
}
//判两线段相交,包括端点和部分重合
int intersect_in(line u,line v) {
	if (!dots_inline(u.a,u.b,v.a)||!dots_inline(u.a,u.b,v.b))
		return !same_side(u.a,u.b,v)&&!same_side(v.a,v.b,u);
	return dot_online_in(u.a,v)||dot_online_in(u.b,v)||dot_online_in(v.a,u)||dot_online_in(v.b,u);
}
int intersect_in(point u1,point u2,point v1,point v2) {
	if (!dots_inline(u1,u2,v1)||!dots_inline(u1,u2,v2))
		return !same_side(u1,u2,v1,v2)&&!same_side(v1,v2,u1,u2);
	return dot_online_in(u1,v1,v2)||dot_online_in(u2,v1,v2)||dot_online_in(v1,u1,u2)||dot_online_in(v2,u1,u2);
}
//判两线段相交,不包括端点和部分重合
int intersect_ex(line u,line v) {
	return opposite_side(u.a,u.b,v)&&opposite_side(v.a,v.b,u);
}
int intersect_ex(point u1,point u2,point v1,point v2) {
	return opposite_side(u1,u2,v1,v2)&&opposite_side(v1,v2,u1,u2);
}
//计算两直线交点,注意事先判断直线是否平行!
//线段交点请另外判线段相交(同时还是要判断是否平行!)
point intersection(line u,line v) {
	point ret=u.a;
	double t=((u.a.x-v.a.x)*(v.a.y-v.b.y)-(u.a.y-v.a.y)*(v.a.x-v.b.x))
	         /((u.a.x-u.b.x)*(v.a.y-v.b.y)-(u.a.y-u.b.y)*(v.a.x-v.b.x));
	ret.x+=(u.b.x-u.a.x)*t;
	ret.y+=(u.b.y-u.a.y)*t;
	return ret;
}
point intersection(point u1,point u2,point v1,point v2) {
	point ret=u1;
	double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
	         /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
	ret.x+=(u2.x-u1.x)*t;
	ret.y+=(u2.y-u1.y)*t;
	return ret;
}
//点到直线上的最近点
point ptoline(point p,line l) {
	point t=p;
	t.x+=l.a.y-l.b.y,t.y+=l.b.x-l.a.x;
	return intersection(p,t,l.a,l.b);
}
point ptoline(point p,point l1,point l2) {
	point t=p;
	t.x+=l1.y-l2.y,t.y+=l2.x-l1.x;
	return intersection(p,t,l1,l2);
}
//点到直线距离
double disptoline(point p,line l) {
	return fabs(xmult(p,l.a,l.b))/distance(l.a,l.b);
}
double disptoline(point p,point l1,point l2) {
	return fabs(xmult(p,l1,l2))/distance(l1,l2);
}
double disptoline(double x,double y,double x1,double y1,double x2,double y2) {
	return fabs(xmult(x,y,x1,y1,x2,y2))/distance(x1,y1,x2,y2);
}
//点到线段上的最近点
point ptoseg(point p,line l) {
	point t=p;
	t.x+=l.a.y-l.b.y,t.y+=l.b.x-l.a.x;
	if (xmult(l.a,t,p)*xmult(l.b,t,p)>eps)
		return distance(p,l.a)<distance(p,l.b)?l.a:l.b;
	return intersection(p,t,l.a,l.b);
}
point ptoseg(point p,point l1,point l2) {
	point t=p;
	t.x+=l1.y-l2.y,t.y+=l2.x-l1.x;
	if (xmult(l1,t,p)*xmult(l2,t,p)>eps)
		return distance(p,l1)<distance(p,l2)?l1:l2;
	return intersection(p,t,l1,l2);
}
//点到线段距离
double disptoseg(point p,line l) {
	point t=p;
	t.x+=l.a.y-l.b.y,t.y+=l.b.x-l.a.x;
	if (xmult(l.a,t,p)*xmult(l.b,t,p)>eps)
		return distance(p,l.a)<distance(p,l.b)?distance(p,l.a):distance(p,l.b);
	return fabs(xmult(p,l.a,l.b))/distance(l.a,l.b);
}
double disptoseg(point p,point l1,point l2) {
	point t=p;
	t.x+=l1.y-l2.y,t.y+=l2.x-l1.x;
	if (xmult(l1,t,p)*xmult(l2,t,p)>eps)
		return distance(p,l1)<distance(p,l2)?distance(p,l1):distance(p,l2);
	return fabs(xmult(p,l1,l2))/distance(l1,l2);
}
//矢量V以P为顶点逆时针旋转angle并放大scale倍
point rotate(point v,point p,double angle,double scale) {
	point ret=p;
	v.x-=p.x,v.y-=p.y;
	p.x=scale*cos(angle);
	p.y=scale*sin(angle);
	ret.x+=v.x*p.x-v.y*p.y;
	ret.y+=v.x*p.y+v.y*p.x;
	return ret;
}
//p点关于直线L的对称点
ponit symmetricalPointofLine(point p, line L) {
	point p2;
	double d;
	d = L.a * L.a + L.b * L.b;
	p2.x = (L.b * L.b * p.x - L.a * L.a * p.x -
	        2 * L.a * L.b * p.y - 2 * L.a * L.c) / d;
	p2.y = (L.a * L.a * p.y - L.b * L.b * p.y -
	        2 * L.a * L.b * p.x - 2 * L.b * L.c) / d;
	return p2;
}
//求两点的平分线
line bisector(point& a, point& b) {
	line ab, ans;
	ab.set(a, b);
	double midx = (a.x + b.x)/2.0,	midy = (a.y + b.y)/2.0;
	ans.a = -ab.b, ans.b = -ab.a, ans.c = -ab.b * midx + ab.a * midy;
	return ans;
}
// 已知入射线、镜面,求反射线。
// a1,b1,c1为镜面直线方程(a1 x + b1 y + c1 = 0 ,下同)系数;
a2,b2,c2为入射光直线方程系数;
a,b,c为反射光直线方程系数.
// 光是有方向的,使用时注意:入射光向量:<-b2,a2>;反射光向量:<b,-a>.
// 不要忘记结果中可能会有"negative zeros"
void reflect(double a1,double b1,double c1,
             double a2,double b2,double c2,
             double &a,double &b,double &c) {
	double n,m;
	double tpb,tpa;
	tpb=b1*b2+a1*a2;
	tpa=a2*b1-a1*b2;
	m=(tpb*b1+tpa*a1)/(b1*b1+a1*a1);
	n=(tpa*b1-tpb*a1)/(b1*b1+a1*a1);
	if(fabs(a1*b2-a2*b1)<1e-20) {
		a=a2;
		b=b2;
		c=c2;
		return;
	}
	double xx,yy; //(xx,yy)是入射线与镜面的交点。
	xx=(b1*c2-b2*c1)/(a1*b2-a2*b1);
	yy=(a2*c1-a1*c2)/(a1*b2-a2*b1);
	a=n;
	b=-m;
	c=m*yy-xx*n;
}

点与线

直线可以用直线上的一个点P0和方向向量v表示

P=P_0+vt其中t为参数(可以通过限制参数来表示线段和射线,长度)

struct Line{//直线定义
    Vector v;
    Point p;
    Line(Vector v, Point p):v(v), p(p) {}
Point get_point_in_line(double t){
//返回直线上一点P = v + (p - v)*t
        return v + (p - v)*t;
    }
};

判断点和直线关系(relation)

利用三点共线的等价条件α × β = 0

直线上取两不同点与待测点构成向量求叉积是否为零来判断点是否在直线上(叉积为0互相平行)

//A, B:直线上一点,C:待判断关系的点

double Cross(Vector A, Vector B) {
	return A.x *  B.y - B.x * A.y;
}
int relation(Point A, Point B, Point C) {
	// 1 left    -1 right      0 in
	int c = sgn(Cross((B - A), (C - A)));
	if(c < 0) return 1;
	else if(c > 0) return -1;
	return 0;
}

计算点 P 到直线 AB 的垂足

inline Point FootPoint(Point p, Point a, Point b) { 
	//点P到直线AB的垂足
	Vector x = p - a, y = p - b, z = b - a;
	double len1 = Dot(x, z) / Length(z), len2 = - 1.0 * Dot(y, z) / Length(z);
	//分别计算AP,BP在AB,BA上的投影
	return a + z * (len1 / (len1 + len2));
	//点A加上向量AF
}

判断点是否在线段上(OnSegment)

bool OnSegment(Point p, Point a1, Point a2) {
	return sgn(Cross(a1-p, a2-p)) == 0 && sgn(Dot(a1-p, a2-p)) < 0;
}

多边形

三角形

 

三角形四心

外心:三边中垂线交点,到三角形三个顶点距离相同(外接圆圆心)

内心:角平分线的交点,到三角形三边的距离相同(内切圆圆心)

垂心:三条垂线的交点

重心:三条中线的交点,到三角形三顶点距离的平方和最小的点,三角形内到三边距离之积最大的点

#include <math.h>
struct point {
	double x,y;
};
struct line {
	point a,b;
};
double distance(point p1,point p2) {
	return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
point intersection(line u,line v) {
	point ret=u.a;
	double t=((u.a.x-v.a.x)*(v.a.y-v.b.y)-(u.a.y-v.a.y)*(v.a.x-v.b.x))
	         /((u.a.x-u.b.x)*(v.a.y-v.b.y)-(u.a.y-u.b.y)*(v.a.x-v.b.x));
	ret.x+=(u.b.x-u.a.x)*t;
	ret.y+=(u.b.y-u.a.y)*t;
	return ret;
}
//外心
point circumcenter(point a,point b,point c) {
	line u,v;
	u.a.x=(a.x+b.x)/2;
	u.a.y=(a.y+b.y)/2;
	u.b.x=u.a.x-a.y+b.y;
	u.b.y=u.a.y+a.x-b.x;
	v.a.x=(a.x+c.x)/2;
	v.a.y=(a.y+c.y)/2;
	v.b.x=v.a.x-a.y+c.y;
	v.b.y=v.a.y+a.x-c.x;
	return intersection(u,v);
}
//内心
point incenter(point a,point b,point c) {
	line u,v;
	double m,n;
	u.a=a;
	m=atan2(b.y-a.y,b.x-a.x);
	n=atan2(c.y-a.y,c.x-a.x);
	u.b.x=u.a.x+cos((m+n)/2);
	u.b.y=u.a.y+sin((m+n)/2);
	v.a=b;
	m=atan2(a.y-b.y,a.x-b.x);
	n=atan2(c.y-b.y,c.x-b.x);
	v.b.x=v.a.x+cos((m+n)/2);
	v.b.y=v.a.y+sin((m+n)/2);
	return intersection(u,v);
}
//垂心
point perpencenter(point a,point b,point c) {
	line u,v;
	u.a=c;
	u.b.x=u.a.x-a.y+b.y;
	u.b.y=u.a.y+a.x-b.x;
	v.a=b;
	v.b.x=v.a.x-a.y+c.y;
	v.b.y=v.a.y+a.x-c.x;
	return intersection(u,v);
}
//重心
//到三角形三顶点距离的平方和最小的点
//三角形内到三边距离之积最大的点
point barycenter(point a,point b,point c) {
	line u,v;
	u.a.x=(a.x+b.x)/2;
	u.a.y=(a.y+b.y)/2;
	u.b=c;
	v.a.x=(a.x+c.x)/2;
	v.a.y=(a.y+c.y)/2;
	v.b=b;
	return intersection(u,v);
}
//费马点
//到三角形三顶点距离之和最小的点
point fermentpoint(point a,point b,point c) {
	point u,v;
	double step=fabs(a.x)+fabs(a.y)+fabs(b.x)+fabs(b.y)+fabs(c.x)+fabs(c.y);
	int i,j,k;
	u.x=(a.x+b.x+c.x)/3;
	u.y=(a.y+b.y+c.y)/3;
	while (step>1e-10)
		for (k=0; k<10; step/=2,k++)
			for (i=-1; i<=1; i++)
				for (j=-1; j<=1; j++) {
					v.x=u.x+step*i;
					v.y=u.y+step*j;
					if (distance(u,a)+distance(u,b)+distance(u,c)>distance(v,a)+distance(v,b)+distance(v,c))
						u=v;
				}
	return u;
}
//求曲率半径 三角形内最大可围成面积
#include<iostream>
#include<cmath>
using namespace std;
const double pi=3.14159265358979;
int main() {
	double a,b,c,d,p,s,r,ans,R,x,l;
	int T=0;
	while(cin>>a>>b>>c>>d&&a+b+c+d) {
		T++;
		l=a+b+c;
		p=l/2;
		s=sqrt(p*(p-a)*(p-b)*(p-c));
		R= s /p;
		if (d >= l)  ans = s;
		else if(2*pi*R>=d) ans=d*d/(4*pi);
		else {
			r = (l-d)/((l/R)-(2*pi));
			x = r*r*s/(R*R);
			ans = s - x + pi * r * r;
		}
		printf("Case %d: %.2lf\n",T,ans);
	}
	return 0;
}

内切圆半径

R =S/P=asin(B/2)sin(C/2)/sin((B+C)/2)

=4Rsin(A/2)sin(B/2)sin(C/2)

=sqrt((P−a)(P−b)(P−c)/P)

=Ptan(A/2)tan(B/2)tan(C/2)

外接圆半径

R=abc/(4S)=a/(2sin(A))=b/(2sin(B))=c/(2sin(C))

海伦公式

三角形边长分别为a、b、c、s1表示的是三角形的周长的一半

S=sqrt(s1∗(s1−a)∗(s1−b)∗(s1−c))

三角形面积

struct point {
	double x,y;
};
//计算cross product (P1-P0)x(P2-P0)
double xmult(point p1,point p2,point p0) {
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
double xmult(double x1,double y1,double x2,double y2,double x0,double y0) {
	return (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
}
//计算三角形面积,输入三顶点
double area_triangle(point p1,point p2,point p3) {
	return fabs(xmult(p1,p2,p3))/2;
}
double area_triangle(double x1,double y1,double x2,double y2,double x3,double y3) {
	return fabs(xmult(x1,y1,x2,y2,x3,y3))/2;
}
//计算三角形面积,输入三边长
double area_triangle(double a,double b,double c) {
	double s=(a+b+c)/2;
	return sqrt(s*(s-a)*(s-b)*(s-c));
}

四边形

D1,D2为对角线,M对角线中点连线,A为对角线夹角

 

a^2+b^2+c^2+d^2=D1^2+D2^2+4M^2

S=D1D2sin(A)/2

圆的内接四边形:

ac+bd=D1D2

S=sqrt((P-a)(P-b)(P-c)(P-d)) ,P为半周长

正n边形

R为外接圆半径,r为内切圆半径

中心角 A=2PI/n

内角 C=(n−2)PI/n

边长 a=2sqrt(R^2-r^2)=2Rsin(A/2)=2rtan(A/2)

面积 S=nar/2=n r^2 tan(A/2)=n R^2 sin(A)/2=n a^2 /(4tan(A/2))

扇形面积

double angle(point O,point A,point B) {
	return acos(dot(O,A,B)/(dis(O,A)*dis(O,B)));
}
//扇形面积alpha*pi*r*r/2*pi
double sectorArea(point O,point A,point B,double r) {
	double alpha=angle(O,A,B);
	return alpha*r*r/2;

弧长 l=rA

弦长 a=2sqrt(2hr-h^2)=2r sin(A/2)

弓形高 h=r-sqrt(r^2-a^2/4)=r(1-cos(A/2))=atan(A/4)/2

扇形面积 S1=r*l/2=r^2 A/2

弓形面积 S2=(r*l-a(r-h))/2=r^2(A-sin(A))/2

已知三边和内切圆或外切圆求面积

S=abc/(4R) 其中R是三角形外接圆半径

S=(a+b+c)∗r/2 (r是三角形内切圆半径)

#include <math.h>
#define eps 1e-8
struct point {
	double x,y;
};
double xmult(point p1,point p2,point p0) {
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
double distance(point p1,point p2) {
	return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
double disptoline(point p,point l1,point l2) {
	return fabs(xmult(p,l1,l2))/distance(l1,l2);
}
point intersection(point u1,point u2,point v1,point v2) {
	point ret=u1;
	double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
	         /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
	ret.x+=(u2.x-u1.x)*t;
	ret.y+=(u2.y-u1.y)*t;
	return ret;
}
//判直线和圆相交,包括相切
int intersect_line_circle(point c,double r,point l1,point l2) {
	return disptoline(c,l1,l2)<r+eps;
}
//判线段和圆相交,包括端点和相切
int intersect_seg_circle(point c,double r,point l1,point l2) {
	double t1=distance(c,l1)-r,t2=distance(c,l2)-r;
	point t=c;
	if (t1<eps||t2<eps)
		return t1>-eps||t2>-eps;
	t.x+=l1.y-l2.y;
	t.y+=l2.x-l1.x;
	return xmult(l1,c,t)*xmult(l2,c,t)<eps&&disptoline(c,l1,l2)-r<eps;
}
//判圆和圆相交,包括相切
int intersect_circle_circle(point c1,double r1,point c2,double r2) {
	return distance(c1,c2)<r1+r2+eps&&distance(c1,c2)>fabs(r1-r2)-eps;
}
//计算圆上到点p最近点,如p与圆心重合,返回p本身
point dot_to_circle(point c,double r,point p) {
	point u,v;
	if (distance(p,c)<eps)
		return p;
	u.x=c.x+r*fabs(c.x-p.x)/distance(c,p);
	u.y=c.y+r*fabs(c.y-p.y)/distance(c,p)*((c.x-p.x)*(c.y-p.y)<0?-1:1);
	v.x=c.x-r*fabs(c.x-p.x)/distance(c,p);
	v.y=c.y-r*fabs(c.y-p.y)/distance(c,p)*((c.x-p.x)*(c.y-p.y)<0?-1:1);
	return distance(u,p)<distance(v,p)?u:v;
}
//计算直线与圆的交点,保证直线与圆有交点
//计算线段与圆的交点可用这个函数后判点是否在线段上
void intersection_line_circle(point c,double r,point l1,point l2,point& p1,point& p2) {
	point p=c;
	double t;
	p.x+=l1.y-l2.y;
	p.y+=l2.x-l1.x;
	p=intersection(p,c,l1,l2);
	t=sqrt(r*r-distance(p,c)*distance(p,c))/distance(l1,l2);
	p1.x=p.x+(l2.x-l1.x)*t;
	p1.y=p.y+(l2.y-l1.y)*t;
	p2.x=p.x-(l2.x-l1.x)*t;
	p2.y=p.y-(l2.y-l1.y)*t;
}
//计算圆与圆的交点,保证圆与圆有交点,圆心不重合
void intersection_circle_circle(point c1,double r1,point c2,double r2,point& p1,point& p2) {
	point u,v;
	double t;
	t=(1+(r1*r1-r2*r2)/distance(c1,c2)/distance(c1,c2))/2;
	u.x=c1.x+(c2.x-c1.x)*t;
	u.y=c1.y+(c2.y-c1.y)*t;
	v.x=u.x+c1.y-c2.y;
	v.y=u.y-c1.x+c2.x;
	intersection_line_circle(c1,r1,u,v,p1,p2);
}
//将向量p逆时针旋转angle角度
Point Rotate(Point p,double angle) {
	Point res;
	res.x=p.x*cos(angle)-p.y*sin(angle);
	res.y=p.x*sin(angle)+p.y*cos(angle);
	return res;
}
//求圆外一点对圆(o,r)的两个切点result1和result2
void TangentPoint_PC(Point poi,Point o,double r,Point &result1,Point &result2) {
	double line=sqrt((poi.x-o.x)*(poi.x-o.x)+(poi.y-o.y)*(poi.y-o.y));
	double angle=acos(r/line);
	Point unitvector,lin;
	lin.x=poi.x-o.x;
	lin.y=poi.y-o.y;
	unitvector.x=lin.x/sqrt(lin.x*lin.x+lin.y*lin.y)*r;
	unitvector.y=lin.y/sqrt(lin.x*lin.x+lin.y*lin.y)*r;
	result1=Rotate(unitvector,-angle);
	result2=Rotate(unitvector,angle);
	result1.x+=o.x;
	result1.y+=o.y;
	result2.x+=o.x;
	result2.y+=o.y;
	return;
}

求两圆相交面积(AreaOfOverlap)

计算两个圆相交所构成的两个扇形面积和减去其构成的筝形的面积

double AreaOfOverlap(Point c1, double r1, Point c2, double r2) {
	double d = Length(c1 - c2);
	if(r1 + r2 < d + eps)
		return 0.0;
	if(d < fabs(r1 - r2) + eps) {
		double r = min(r1, r2);
		return pi*r*r;
	}
	double x = (d*d + r1*r1 - r2*r2)/(2.0*d);
	double p = (r1 + r2 + d)/2.0;
	double t1 = acos(x/r1);
	double t2 = acos((d - x)/r2);
	double s1 = r1*r1*t1;
	double s2 = r2*r2*t2;
	double s3 = 2*sqrt(p*(p - r1)*(p - r2)*(p - d));
	return s1 + s2 - s3;
}

圆锥

母线l=sqrt(h^2+r^2)

侧面积 S=PI *r*l

全面积 T=PI*r*(l+r)

体积 V=PI*r^2*h/3

已知圆锥表面积S求最大体积V

V = S * sqrt(S / (72 * Pi))

全面积 T=4PI*r^2

体积 V=4PI*r^3/3

求多边形的重心

#include<stdio.h>
int main() {
	int n,i;
	int x1,y1,x2,y2,x3,y3;
	double sum_x=0,sum_y=0,sum_s=0;
	scanf("%d",&n);
	scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
	for(i=1; i<=n-2; i++) {
		scanf("%d%d",&x3,&y3);
		double s=((x2-x1)*(y3-y1)-(x3-x1)*(y2-y1))/2.0;
		sum_x+=(x1+x2+x3)*s;
		sum_y+=(y1+y2+y3)*s;
		sum_s+=s;
		x2=x3;
		y2=y3;
	}
	printf("%.2lf %.2lf\n",sum_x/sum_s/3.0,sum_y/sum_s/3.0);
	return 0;
}

求多边形面积

//求凸多边形的有向面积
//叉积的几何意义就是三角形有向面积的二倍
//所以这里要除以二
double convex_polygon_area(Point* p, int n) {
	double area = 0;
	for(int i = 1; i <= n - 2; ++ i)
		area += Cross(p[i] - p[0], p[i + 1] - p[0]);
	return area / 2;
	//return fabs(area / 2);
//不加的话求的是有向面积,逆时针为负,顺时针为正
}
//求非凸多边形的有向面积
//我们叉积求得的三角形面积是有向的,
//在外面的面积可以正负抵消掉,
//因此非凸多边形也适用,可以从任意点出发划分
//可以取原点为起点,减少叉乘次数
double polyg_on_area(Point* p, int n) {
	double area = 0;
	for(int i = 1; i <= n - 2; ++ i)
		area += Cross(p[i] - p[0], p[i + 1] - p[0]);
	return area / 2;
}

凸包

Graham Scan

时间复杂度为O(nlogn)

const int maxn = 1e3 + 5;
struct Point {
	double x, y;
	Point(double x = 0, double y = 0):x(x),y(y) {}
};
typedef Point Vector;
Point lst[maxn];
int stk[maxn], top;
Vector operator - (Point A, Point B) {
	return Vector(A.x-B.x, A.y-B.y);
}
int sgn(double x) {
	if(fabs(x) < eps)
		return 0;
	if(x < 0)
		return -1;
	return 1;
}
double Cross(Vector v0, Vector v1) {
	return v0.x*v1.y - v1.x*v0.y;
}
double Dis(Point p1, Point p2) { //计算 p1p2的 距离
	return sqrt((p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(Point p1, Point p2) { //极角排序函数 ,角度相同则距离小的在前面
	int tmp = sgn(Cross(p1 - lst[0], p2 - lst[0]));
	if(tmp > 0)
		return true;
	if(tmp == 0 && Dis(lst[0], p1) < Dis(lst[0], p2))
		return true;
	return false;
}
//点的编号0 ~ n - 1
//返回凸包结果stk[0 ~ top - 1]为凸包的编号
void Graham(int n) {
	int k = 0;
	Point p0;
	p0.x = lst[0].x;
	p0.y = lst[0].y;
	for(int i = 1; i < n; ++i) {
		if( (p0.y > lst[i].y) || ((p0.y == lst[i].y) && (p0.x > lst[i].x)) ) {
			p0.x = lst[i].x;
			p0.y = lst[i].y;
			k = i;
		}
	}
	lst[k] = lst[0];
	lst[0] = p0;
	sort(lst + 1, lst + n, cmp);
	if(n == 1) {
		top = 1;
		stk[0] = 0;
		return ;
	}
	if(n == 2) {
		top = 2;
		stk[0] = 0;
		stk[1] = 1;
		return ;
	}
	stk[0] = 0;
	stk[1] = 1;
	top = 2;
	for(int i = 2; i < n; ++i) {
		while(top > 1 && Cross(lst[stk[top - 1]] - lst[stk[top - 2]], lst[i] - lst[stk[top - 2]]) <= 0)
			--top;
		stk[top] = i;
		++top;
	}
	return ;
}

Andrew算法

时间复杂度为O(nlogn)

struct Point {
	double x, y;
	Point(double x = 0, double y = 0):x(x),y(y) {}
};
typedef Point Vector;
Vector operator - (Point A, Point B) {
	return Vector(A.x-B.x, A.y-B.y);
}
bool operator < (const Point& a, const Point& b) {
	if(a.x == b.x)
		return a.y < b.y;
	return a.x < b.x;
}
double Cross(Vector v0, Vector v1) {
	return v0.x*v1.y - v1.x*v0.y;
}
//计算凸包,输入点数组为 p,个数为 n, 输出点数组为 ch。函数返回凸包顶点数
//如果不希望凸包的边上有输入点,则把两个 <= 改为 <
//在精度要求高时建议用dcmp比较
//输入不能有重复点,函数执行完后输入点的顺序被破坏
int ConvexHull(Point* p, int n, Point* ch) {
	sort(p, p+n);
	int m = 0;
	for(int i = 0; i < n; ++i) {
		while(m > 1 && Cross(ch[m-1] - ch[m-2], p[i] - ch[m-2]) < 0) {
			m--;
		}
		ch[m++] = p[i];
	}
	int k = m;
	for(int i = n-2; i>= 0; --i) {
		while(m > k && Cross(ch[m-1] - ch[m-2], p[i] - ch[m-2]) < 0) {
			m--;
		}
		ch[m++] = p[i];
	}
	if(n > 1)
		--m;
	return m;
}

Minimal Circle最小圆覆盖问题

#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int mm=111;
typedef double diy;
struct point {
	diy x,y;
} g[mm];
diy Sqr(diy x) {
	return x*x;
}
diy Dis(point P,point Q) {
	return sqrt(Sqr(P.x-Q.x)+Sqr(P.y-Q.y));
}
void Circle(point P0,point P1,point P2,point &o) {
	diy a1=P1.x-P0.x,b1=P1.y-P0.y,c1=(Sqr(a1)+Sqr(b1))/2;
	diy a2=P2.x-P0.x,b2=P2.y-P0.y,c2=(Sqr(a2)+Sqr(b2))/2;
	diy d=a1*b2-a2*b1;
	o.x=P0.x+(c1*b2-c2*b1)/d;
	o.y=P0.y+(a1*c2-a2*c1)/d;
}
void MinCircle(point g[],point &o,diy &r,int n) {
	random_shuffle(g,g+n);
	int i,j,k;
	o=g[0];
	for(r=0,i=1; i<n; ++i) {
		if(Dis(g[i],o)<=r)continue;
		o=g[i];
		for(r=j=0; j<i; ++j) {
			if(Dis(g[j],o)<=r)continue;
			o.x=(g[i].x+g[j].x)/2;
			o.y=(g[i].y+g[j].y)/2;
			r=Dis(o,g[i]);
			for(k=0; k<j; ++k) {
				if(Dis(g[k],o)<r)continue;
				Circle(g[i],g[j],g[k],o);
				r=Dis(o,g[i]);
			}
		}
	}
}
int main() {
	int i,n;
	point o;
	diy r;
	while(scanf("%d",&n),n) {
		for(i=0; i<n; ++i)
			scanf("%lf%lf",&g[i].x,&g[i].y);
		MinCircle(g,o,r,n);
		printf("%.2lf %.2lf %.2lf\n",o.x,o.y,r);
	}
	return 0;
}

A Triangle and a Circle三角形与圆相交求面积

题意:给出一个三角形各点坐标以及一个圆的圆心坐标和其半径,求三角形与该圆的相交面积

输入包含多组样例,以文件尾结束,每组用例包括9个浮点数x1,y1,x2,y2,x3,y3,x0,y0,r分别表示三角形三点坐标,圆心坐标以及圆的半径

对于每组用例,输出一行,为多边形与圆相交部分的面积。所有输出中浮点数保留小数点后两位

point_t O,A,B,C;
double Radius;
int main() {
	while(EOF!=scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y,&C.x,&C.y,&O.x,&O.y,&Radius)) {
		double ans = 0.0;
		ans += triangleAndCircleArea(O,A,B,Radius);
		ans += triangleAndCircleArea(O,B,C,Radius);;
		ans += triangleAndCircleArea(O,C,A,Radius);
		printf("%.2f\n",fabs(ans));
	}
}

area多边形与圆相交求面积

题意:有一个凸多边形岛屿,然后告诉你从高空(x,y,h)投下炸弹,爆炸半径r,飞机水平速度和重力加速度,问炸了岛屿多少面积

题解:算出来岛屿落地位置,再利用圆与凸多边形面积交

#include <bits/stdc++.h>
using namespace std;
double const PI = acos(-1.0);
double const EPS = 1E-8;

inline int sign(double x) {
	if(x>EPS) return 1;
	return x<-EPS?-1:0;
}
///返回[-90,90] 
inline double myasin(double x) {
	if(x>1.0) x=1.0;
	if(x<-1.0) x=-1.0;
	return asin(x);
}
//求解一元二次方程根,返回根的数量,小根在前,大根在后
inline int root(double a,double b,double c,double&x1,double&x2) {
	double delta = b*b-4.0*a*c;
	int tmp = sign(delta);
	if(tmp<0) return 0;
	if(0==tmp) {
		x1 = x2 = -b / ( a + a );
		return 1;
	}
	delta = sqrt(delta);
	x1 = ( -b - (delta) ) / ( a + a );
	x2 = ( -b + (delta) ) / ( a + a );
	return 2;
}
//点的数据结构
struct point_t {
	double x,y;
	point_t(int a=0,int b=0):x(a),y(b) {}
};
//叉积OA×OB
double cross(point_t const&O,point_t const&A,point_t const&B) {
	double xoa = A.x - O.x;
	double yoa = A.y - O.y;
	double xob = B.x - O.x;
	double yob = B.y - O.y;
	return xoa * yob - xob * yoa;
}
double dot(point_t const&O,point_t const&A,point_t const&B) {
	double xoa = A.x - O.x;
	double yoa = A.y - O.y;
	double xob = B.x - O.x;
	double yob = B.y - O.y;
	return xoa * xob + yob * yoa;
}
double dist2(point_t const&A,point_t const&B) {
	double x = A.x - B.x;
	double y = A.y - B.y;
	return x*x+y*y;
}
//返回方向角,在(-180,180]之间,O、A、B各不相同
inline double getAngle(point_t const&O,point_t const&A,point_t const&B) {
	double area = cross(O,A,B)*0.5;
	double ddot = dot(O,A,B);
	if(0==sign(area)) { //OAB成一条直线
		if(sign(ddot)>0) return 0.0;
		return PI;
	}
	double OA = sqrt(dist2(O,A));
	double OB = sqrt(dist2(O,B));
	//首先求角度
	double theta = myasin(area*2.0/(OA*OB));
	if(sign(ddot)>=0)return theta;//说明是锐角,直接返回
	//正的钝角
	if(sign(area)>0) return PI-theta;
	//负的钝角
	return -theta - PI;
}
//求三角形OAB与圆O相交的有向面积,A、B必然不同
//令AB与圆的交点为P,则P=(1-t)A+tB,且|OP|^2=radius^2
//可以解一个关于t的一元二次方程
//[t(B.x-A.x)+(A.x-O.x)]^2+[t(B.y-A.y)+(A.y-O.y)]^2 = radius^2
double triangleAndCircleArea(point_t const&O,point_t const&A,point_t const&B,double radius) {
	double area = cross(O,A,B)*0.5;
	int s = sign(area);
	if(0==s) return 0.0;
	double a = (B.x-A.x)*(B.x-A.x) + (B.y-A.y)*(B.y-A.y);
	double b = 2.0*( (B.x-A.x)*(A.x-O.x)+(B.y-A.y)*(A.y-O.y) );
	double c = (A.x-O.x)*(A.x-O.x)+(A.y-O.y)*(A.y-O.y)-radius*radius;
	double x1,x2;
	int cnt = root(a,b,c,x1,x2);
	double OA = sqrt(dist2(O,A));
	double OB = sqrt(dist2(O,B));
	double AB = sqrt(dist2(A,B));
	//直线A、B与圆无交点,就是扇形面积,只有1个交点也是扇形面积
	if(0==cnt||1==cnt) {
		double theta = getAngle(O,A,B);
		return 0.5*radius*radius*theta;
	}
	/**两个根,[0,1]区间将整个实数分为三段,一共有6种可能性*/
	//两个根都不在[0,1]范围内,且在同一边,还是扇形面积
	if( sign(x2) < 0 || sign(x1-1.0) > 0 ) {
		double theta = myasin(area*2.0/(OA*OB));
		return 0.5*radius*radius*theta;
	}
	//两个根都不在[0,1]范围内,且各在一边,说明A、B在圆内
	if( sign(x1) < 0 && sign(x2-1.0) > 0 ) {
		return area;
	}
	//说明x1不在、x2在范围内,对应A在圆内、B在圆外的情况
	//此时面积是三角形OAP加扇形
	if( sign(x1) < 0 && sign(x2) >= 0 && sign(x2-1.0) <= 0 ) {
		double theta = myasin(area*(1.0-x2)*2.0/(radius*OB));
		return area*x2 + 0.5*radius*radius*theta;
	}
	//说明x2不在、x1在范围内,对应B在圆内、A在圆外的情况
	//此时面积是三角形OPB加扇形
	if( sign(x1) >= 0 && sign(x1-1.0) <= 0 && sign(x2-1.0) > 0 ) {
		double theta = myasin(area*x1*2.0/(radius*OA));
		return area * (1.0-x1) + 0.5 * radius * radius * theta;
	}
	//x1和x2都在A、B之间,此时面积是两个扇形加一个三角形
	double theta1 = myasin(area*x1*2.0/(radius*OA));
	double theta2 = myasin(area*(1.0-x2)*2.0/(radius*OB));
	return area*(x2-x1) + 0.5*radius*radius*(theta1+theta2);
}
point_t O,P[100100];
double Radius;
point_t T,Speed;
double H;
int main() {
	//freopen("1.txt","r",stdin);
	while(EOF!=scanf("%lf%lf%lf%lf%lf%lf",&T.x,&T.y,&H,&Speed.x,&Speed.y,&Radius)) {
		int n;
		scanf("%d",&n);
		for(int i=0; i<n; ++i)scanf("%lf%lf",&P[i].x,&P[i].y);
		P[n] = P[0];
		//求下落时间
		double t = sqrt(H/5.0);
		//求圆心
		O.x = T.x + Speed.x * t;
		O.y = T.y + Speed.y * t;
		double ans = 0.0;
		for(int i=0; i<n; ++i) {
			ans += triangleAndCircleArea(O,P[i],P[i+1],Radius);
		}
		printf("%.2f\n",fabs(ans));
	}
}

离散化

区域的个数

w×h 的的格子画了n条或垂直或水平宽度为1的直线,求出这些格子被划分成了多少个4连块(上、下、左、右连通)。

 

【输入格式】

第一行包含两个整数:w和h,表示矩形的列数和行数(行列编号都从1开始)。

第二行包含一个整数n,表示有n条直线。

接下来的n行,每行包含四个整数:x1,y1,x2,y2,表示一条直线的列号和行号。

【输出格式】一个整数,表示区域数量。

【输入样例】

10 10

5

1 4 6 4

1 8 10 8

4 1 4 10

9 1 9 5

10 6 10 10

【输出样例】6

【数据范围】1<=w,h<=1000000 , 1<=n<=500

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e2 + 5;
int x1[maxn], x2[maxn], y1[maxn], y2[maxn];//开始列号结束列号,开始行号,结束行号
int w, h, n, ans;//宽,高,以及横线的个数
bool line[maxn*6][maxn*6];
int dire[5][3]= {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
struct node {
	int x, y;
	node(int x, int y):x(x), y(y) {}
};
//对x1和x2进行离散化,并返回离散化之后的宽度
int compress(int *xx1,int *xx2,int w) { //开始坐标,结束坐标
	vector<int> v;
	for(int i = 0; i < n; ++i) //将横线本身以及附近两横线存储
		for(int d = -1; d <= 1; ++d) {
			int nx1 = xx1[i] + d;
			int nx2 = xx2[i] + d;
			if(nx1 >= 1 && nx1 <= w) v.push_back(nx1);
			if(nx2 >= 1 && nx2 <= w) v.push_back(nx2);
		}
	//去重
	sort(v.begin(), v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	//unique()函数去重并返回多余元素存储的起始位置
	//erase()函数区间删除左开右闭的区间
	//离散化后的坐标
	for(int i = 0; i < n; ++i) {
		xx1[i] = find(v.begin(), v.end(), xx1[i]) - v.begin();
		xx2[i] = find(v.begin(), v.end(), xx2[i]) - v.begin();
		//通过find()函数将坐标转化为了下标
	}
	return v.size();
}
void bfs(int xx,int yy) {
	queue<node> q;
	q.push(node {xx, yy});
	line[xx][yy] = 1;
	while(!q.empty()) {
		node t=q.front();
		q.pop();
		int x=t.x;
		int y=t.y;
		for(int i=0; i<4; i++) {
			int nx=x+dire[i][0];
			int ny=y+dire[i][1];
			if(nx<0||nx>=h||ny<0||ny>=w)continue;
			if(!line[nx][ny]) {
				line[nx][ny]=1;
				q.push(node(nx, ny));
			}
		}
	}
	return ;
}
int main() {
	while(cin >> w >> h >> n) {
		ans = 0;
		memset(line, false, sizeof(line));
		for(int i = 0; i < n; ++i) cin >> x1[i];
		for(int i = 0; i < n; ++i) cin >> x2[i];
		for(int i = 0; i < n; ++i) cin >> y1[i];
		for(int i = 0; i < n; ++i) cin >> y2[i];
		w = compress(x1, x2, w);
		h = compress(y1, y2, h);
		//标记上所在横线上的点
		for(int i = 0; i < n; ++i) //枚举n条横线
			for(int y = y1[i]; y <= y2[i]; ++y) //枚举行
				for(int x = x1[i]; x <= x2[i]; ++x) //枚举列
					line[y][x] = true;
		//打印查看离散化后的图形
		/*  for(int i=0;i<h;i++)
		{
			for(int j=0;j<w;j++)
			{
				if(line[i][j])cout<<1<<" ";
				else cout<<0<<" ";
		 }
		 cout<<endl;
		}
		cout<<endl<<ans<<endl;  */
		//搜索求区域块数 防止爆栈,这里使用广搜
		for(int i = 0; i < h; ++i) {
			for(int j = 0; j < w; ++j) {
				if(!line[i][j]) {
					++ans;
					bfs(i,j);
				}
			}
		}
		cout<<ans<<endl;
	}

	return 0;
}

一些函数/定理/应用

Pick定理(根据点在多边形内和边界的个数求面积)

格点就是横纵坐标相等的点

皮克定理是指一个计算点阵中顶点在格点上的多边形面积公式

2S=2a+b−2

a表示多边形内部的点数,b表示多边形边界上的点数,S表示多边形的面积

判断四点共面(混合积)

struct  point {
	double x, y, z;
	point  operator - (point &o) {
		point  ans;
		ans.x = this->x - o.x;
		ans.y = this->y - o.y;
		ans.z = this->z - o.z;
		return ans;
	}
};
double  dot_product(const point &a, const point &b) {
	return a.x * b.x + a.y * b.y + a.z * b.z;
}
point cross_product(const point &a, const point &b) {
	point  ans;
	ans.x = a.y * b.z - a.z * b.y;
	ans.y = a.z * b.x - a.x * b.z;
	ans.z = a.x * b.y - a.y * b.x;
	return ans;
}
int main() {
	point p[4];
	int T;
	for (scanf("%d", &T); T--;) {
		for (int i = 0; i < 4; ++i) {
			scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z);
		}
		puts(dot_product(p[3] - p[0], cross_product(p[2] - p[0], p[1] - p[0])) == 0.0 ? "Yes\n" : "No\n");
	}
	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵩韵儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值