ACM暑期集训10

今天主要学习力计算几何基础。

以下主要给出计算几何的一些算法的代码,而分析问题,知道所需算法也十分重要。

1.凸包

1)先上凸包需要的数据结构

完善点的数据结构:
struct Point {
	double x, y;
	Point(double x=0, double y=0): x(x), y(y) {}					//构造函数
	Point operator + (Point p) {return Point(x+p.x, y+p.y);}				//两向量相加
	Point operator - (Point p) {return Point(x-p.x, y-p.y);}				//两向量相减
	Point operator * (double k) {return Point(k*x, k*y);}				//向量的数乘
	Point operator / (double k) {return Point(x/k, y/k);}				//向量的数乘(乘以一个分数)
	bool operator < (const Point &p) const {return x!=p.x ? (x<p.x):(y<p.y);}	//给点排序时使用(可以根据需要变化)
	double norm() {return x*x + y*y;}							//向量的范数
	double abs() {return sqrt(norm());}							//向量的大小
	double dot(Point p) {return x*p.x + y*p.y;}					//两向量内积
	double cross(Point p) {return x*p.y - y*p.x;}					//两向量外积
};

向量:
typedef Point Vector;

多边形:
typedef vector<Point> Polygon;

2)判断点与向量的位置关系

int ccw(Point p0, Point p1, Point p2) {
	Vector v1 = p1-p0;
	Vector v2 = p2-p0;
	if(v1.cross(v2) > 0) return 1;			//逆时针方向
	if(v1.cross(v2) < 0) return -1;			//顺时针方向
	if(v1.dot(v2) < 0) return 2;			//同一直线上的反方向
	if(v1.norm() < v2.norm()) return -2;	//同一直线上的正方向
	return 0;								//在向量上
}

3)安德鲁算法O(n*log(n))

Polygon convexHull(Polygon source) {
	if(source.size() < 3) return source;	//当点的个数少于三个,直接返回原点集
	Polygon upper, lower;			//凸包的上下部分
	sort(source.begin(), source.end());	//原点集内点的排序
	upper.push_back(source[0]);
	upper.push_back(source[1]);
	lower.push_back(source[source.size()-1]);
	lower.push_back(source[source.size()-2]);
	for(int i=2; i<source.size(); ++i) {	//构建凸包上部
		for(int n=upper.size()-1; n>=1 && ccw(upper[n-1], upper[n], source[i])!=-1; --n)
			upper.pop_back();		//当上部出现不合适的点时删除
		upper.push_back(source[i]);
	}
	for(int i=source.size()-3; i>=0; --i) {	//构建凸包下部
		for(int n=lower.size()-1; n>=1 &&ccw(lower[n-1], lower[n], source[i])!=-1; --n)
			lower.pop_back();		//当下部出现不合适的点时删除
		lower.push_back(source[i]);
	}
	for(int i=1; i<lower.size()-1; ++i)		//上下合并
		upper.push_back(lower[i]);
	return upper;
}

2.其他数据结构

线段:
struct Segment {
    Point p1, p2;
};

直线:
typedef Segment Line;

圆:
struct Circle {
    Point o;			//圆心
    double radius;		//半径
}

3.其他算法

1)直线的正交和平行判断

首先求出两条直线对应的向量

两向量的内积为0,则两直线正交(相互垂直)

两向量外积为0,则两直线平行

2)投影

Point project(Segment s, Point p) {
	Vector base = s.p2 - s.p1;
	double r = (p-s.p1).dot(base)/base.norm();
	return s.p1 + base*r;
}

3)映像(关于直线的对称点)

Point reflect(Segment s, Point p) {
	return p + (project(s, p) - p)*2.0;
}

4)距离

两点间距离:略

点与直线的距离:
double getDist(Line l, Point p) {
	return abs((l.p2-l.p1).cross(p-l.p1) / (l.p2-l.p1).abs());
}

点与线段的距离:
double getDist(Segment s, Point p) {
	if((s.p2-s.p1).dot(p-s.p1) < 0) return (p-s.p1).abs();
	if((s.p1-s.p2).dot(p-s.p2) < 0) return (p-s.p2).abs();
	return abs((s.p2-s.p1).cross(p-s.p1) / (s.p2-s.p1).abs());
}

线段之间的距离:

每条线段的两个端点到另一条线段的距离中的最小值

5)判断两线段是否相交

bool intersect(Point p1, Point p2, Point p3, Point p4) {
	return ccw(p1, p2, p3)*ccw(p1, p2, p4)<=0 &&
			ccw(p3, p4, p1)*ccw(p3, p4, p2)<=0;
}

6)求两线段的交点

Point getCrossPoint(Segment s1, Segment s2) {
	Vector base = s2.p2 - s2.p1;
	double d1 = abs(base.cross(s1.p1-s2.p1));
	double d2 = abs(base.cross(s1.p2-s2.p1));
	double t = d1/(d1+d2);
	return s1.p1 + (s1.p2 - s1.p1)*t;
}

7)计算凸多边形的面积

double getArea(Polygon polygon) {
	double res = 0;
	for(int i=0; i<polygon.size(); ++i)
		res += polygon[i].cross(polygon[(i+1)%polygon.size()]);
	return res/2;							//一定要记得除以2
}

8)判断一个点是否在多边形内部

int contains(Polygon g, Point p) {
	int n = g.size();
	bool flag = false;
	for(int i=0; i<n; ++i) {
		Point a = g[i]-p, b = g[(i+1)%n]-p;
		if(ccw(g[i], g[(i+1)%n], p) == 0) return 1;			//1表示在“上”
		if(a.y > b.y) swap(a, b);
		if(a.y<0 && b.y>0 && a.cross(b)>0) flag = !flag;
	}
	return flag ? 2 : 0;								//2表示在“内”,0表示在“外”
}

9)

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值