计算几何总结点、线段、直线

叉积

老是搞不清楚,叉积怎么判断方向的,后来发现就是右手定则的事,看大拇指的方向指向纸面外还是纸面里:

指 向 纸 面 外 ⇔ 叉 积 大 于 0 ⇔ 逆 时 针 \footnotesize 指向纸面外\Leftrightarrow叉积大于0\Leftrightarrow逆时针 0
指 向 纸 面 里 ⇔ 叉 积 小 于 0 ⇔ 顺 时 针 \footnotesize 指向纸面里\Leftrightarrow叉积小于0\Leftrightarrow顺时针 0

x ⃗ × y ⃗ > 0 : \vec{x}\times \vec{y}>0: x ×y >0:
计算几何总结点、线段、直线_第1张图片
x ⃗ × y ⃗ < 0 : \vec{x}\times \vec{y}<0: x ×y <0:
计算几何总结点、线段、直线_第2张图片

应用

x ⃗ × y ⃗ = 0 : x ⃗ 和 y ⃗ 共 线 \vec{x}\times \vec{y}=0: \vec{x}和\vec{y} 共线 x ×y =0:x y 线
判断点与直线的位置关系

题目

TOYS
计算几何总结点、线段、直线_第3张图片
遇到第一个L[i].s P × L[i].s L[i].s < 0 时,i - 1所在的格子玩具个数+1


#include 
#include 
#include 
#include 
#include 
#pragma warning(disable:4996)

using namespace std;

int sgn(double x) {
     
	if (fabs(x) < eps) return 0;
	if (x < 0) return -1;
	else return 1;
}

struct Point {
     
	double x;
	double y;
	Point(double _x = 0, double _y = 0) {
     
		x = _x;
		y = _y;
	}
	Point operator - (Point b) {
     
		return Point{
      x - b.x, y - b.y };
	}
};

struct Line {
     
	Point e, s;
	Line() {
     }
	Line(Point _e, Point _s) {
     
		e = _e;
		s = _s;
	}
	// 注意有两个 const
	bool operator < (const Line &b) const {
     
		return s.x < b.s.x;
	}
};

const double eps = 1e-8;
const int N = 1e4 + 5;

Line L[N];
int ans[N];

double chaji(Point a, Point b, Point c)
{
     
	Point x = b - a;	// 向量ab
	Point y = c - a;	// 向量ac
	return x.x * y.y - x.y * y.x;
}

int main()
{
     
	int n, m;
	double x1, y1, x2, y2;
	while (scanf("%d", &n) && n) {
     
		scanf("%d", &m);
		scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
		double u1, u2;
		for (int i = 1; i <= n; ++i) {
     
			scanf("%lf%lf", &u1, &u2);
			L[i].e = Point{
      u1, y1 };
			L[i].s = Point{
      u2, y2 };
		}
		memset(ans, 0, sizeof(ans));
		L[0].e = Point{
      x1, y1 };
		L[0].s = Point{
      x1, y2 };
		L[n + 1].e = Point{
      x2, y1 };
		L[n + 1].s = Point{
      x2, y2 };
		Point p;
		for (int i = 1; i <= m; ++i) {
     
			scanf("%lf%lf", &p.x, &p.y);
			for (int j = 0; j <= n + 1; ++j) {
     
				if (sgn(chaji(L[j].s, p, L[j].e)) < 0) {
     
					++ans[j - 1];
					break;
				}
			}
		}

		for (int i = 0; i <= n; ++i)
			printf("%d: %d\n", i, ans[i]);
		printf("\n");
	}
	system("pause");
	return 0;
}

直线与线段相交

跨立实验:
计算几何总结点、线段、直线_第4张图片
e − s → × v . s − s → \overrightarrow{e-s}\times\overrightarrow{v.s-s} es ×v.ss :大于0(小于0),右手定则指向纸面外(内),逆(顺)时针
e − s → × v . s − s → \overrightarrow{e-s}\times\overrightarrow{v.s-s} es ×v.ss :小于0(大于0),右手定则指向纸面内(外),顺(逆)时针

e − s → × v . s − s → \overrightarrow{e-s}\times\overrightarrow{v.s-s} es ×v.ss e − s → × v . e − s → \overrightarrow{e-s}\times\overrightarrow{v.e-s} es ×v.es 异号,所以它们的叉积小于零:
e − s → × v . s − s → ⋅ e − s → × v . e − s → < 0 \overrightarrow{e-s}\times\overrightarrow{v.s-s} \cdot \overrightarrow{e-s}\times\overrightarrow{v.e-s} < 0 es ×v.ss es ×v.es <0

题目

Segments

#include 
#include 
#include 
#include 
#include 
#pragma warning(disable:4996)

using namespace std;

const double eps = 1e-8;
const int N = 105;

int sgn(double x) {
     
	if (fabs(x) < eps) return 0;
	if (x < 0) return -1;
	else return 1;
}

struct Point {
     
	double x, y;
	Point() {
     }
	Point(double _x, double _y) {
     
		x = _x;
		y = _y;
	}
	double operator ^ (const Point &b) const {
     
		return x * b.y - y * b.x;
	}
	Point operator - (const Point &b) const {
     
		return Point(x - b.x, y - b.y);
	}
	double operator * (const Point &b) const {
     
		return x * b.x + y * b.y;
	}
	bool operator == (Point b) const {
     
		return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
	}
};

struct Line {
     
	Point s, e;
	Line() {
     }
	Line(Point _s, Point _e) {
     
		s = _s;
		e = _e;
	}
	// 跨立实验,判断线段与直线的交点
	int segcrossseg(Line v) {
     
		int d1 = sgn((e - s) ^ (v.s - s));
		int d2 = sgn((e - s) ^ (v.e - s));
		if ((d1 ^ d2) == -2) return 2;
		return (d1 == 0 || d2 == 0);
	}
};
Point p[N << 1];
Line L[N];

int main()
{
     
	int T;
	scanf("%d", &T);
	while (T--)
	{
     
		int n, cnt = 0;
		scanf("%d", &n);
		double x1, y1, x2, y2;
		for (int i = 1; i <= n; ++i) {
     
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			p[++cnt] = Point{
      x1, y1 };
			p[++cnt] = Point{
      x2, y2 };
			L[i] = Line{
      p[cnt - 1], p[cnt] };
		}
		bool flag = true;
		for (int i = 1; i <= cnt; ++i) {
     
			for (int j = i + 1; j <= cnt; ++j) {
     
				if (!(p[i] == p[j])) {
     
					flag = true;
					for (int k = 1; k <= n; ++k) {
     
						if (Line(p[i], p[j]).segcrossseg(L[k]) == 0) {
     
							flag = false;
							break;
						}
					}
					if (flag) break;
					else continue;
				}
				else continue;
				if (flag) break;
			}
			if (flag) break;
		}
		if (flag) printf("Yes!\n");
		else printf("No!\n");
	}
	system("pause");
	return 0;
}

直线与直线相交

1)判断是否平行:叉积判断
2)平行判断是否重合:通过叉积判断一个直线上一点是否在另一个直线上
3)不平行且不重合求交点

题目

Intersecting Lines

#include 
#include 
#include 
#include 
#include 
#pragma warning(disable:4996)

using namespace std;

const double eps = 1e-10;

int sgn(double x) {
     
	if (fabs(x) < eps) return 0;
	if (x < 0) return -1;
	else return 1;
}

struct Point {
     
	double x, y;
	Point() {
     }
	Point(double _x, double _y) {
     
		x = _x;
		y = _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;
	}
	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);
	}
};

struct Line {
     
	Point s, e;
	Line() {
     }
	Line(Point _s, Point _e) {
     
		s = _s;
		e = _e;
	}
	// 点和直线的关系
	int relation(Point p) {
     
		int c = sgn((p - s) ^ (e - s));
		if (c < 0) return 1;
		else if (c > 0) return 2;
		else return 3;
	}
	// 直线平行
	bool parallel(Line v) {
     
		return sgn((e - s) ^ (v.e - v.s)) == 0;
	}
	// 直线关系,0:平行,1:重合,2:相交
	int linecrossline(Line v) {
     
		if ((*this).parallel(v))
			return v.relation(s) == 3;
		return 2;
	}
	// 求两直线的交点
	Point crosspoint(Line v) {
     
		double a1 = (v.e - v.s) ^ (s - v.s);
		double a2 = (v.e - v.s) ^ (e - v.s);
		return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1));
	}
};

int main()
{
     
	int T;
	while (scanf("%d", &T) != EOF) {
     
		printf("INTERSECTING LINES OUTPUT\n");
		while (T--)
		{
     
			double x1, y1, x2, y2;
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			Line L1(Point(x1, y1), Point(x2, y2));
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			Line L2(Point(x1, y1), Point(x2, y2));
			int n = L1.linecrossline(L2);
			if (n == 0)	printf("NONE\n");
			else if (n == 1) printf("LINE\n");
			else {
     
				Point p = L1.crosspoint(L2);
				printf("POINT %.2lf %.2lf\n", p.x, p.y);
			}
		}
		printf("END OF OUTPUT\n");
	}
	//system("pause");
	return 0;
}

线段与线段相交

两次跨立实验判断

题目

The Doors
线段是否是规范相交判断+求最短路
建图:
计算几何总结点、线段、直线_第5张图片
算法
存储所有墙的端点(除了和周围墙的交点),存储所有墙的线段

选取任意两个不同的端点,判断和所有线段是否存在规范相交,如果不存在则建图,权值为两点之间的距离。

建完图之后跑最短路算法(Floyd或者Dijkstra算法)

#include 
#include 
#include 
#include 
#include 
#include 
#pragma warning(disable:4996)

using namespace std;

const double eps = 1e-8;
const int N = 1e3 + 5;
const double inf = 1e9;

int sgn(double x) {
     
	if (fabs(x) < eps) return 0;
	if (x < 0) return -1;
	else return 1;
}

struct Point {
     
	double x, y;
	Point() {
     }
	Point(double _x, double _y) {
     
		x = _x;
		y = _y;
	}

	double operator * (const Point &b) const {
     
		return x *b.x + y * b.y;
	}
	
	double operator ^ (const Point &b) const {
     
		return x * b.y - y * b.x;
	}
	
	Point operator - (const Point &b) const {
     
		return Point(x - b.x, y - b.y);
	}

	double distance(Point p) {
     
		return hypot(x - p.x, y - p.y);
	}
};

struct Line {
     
	Point s, e;
	Line() {
     }
	Line(Point _s, Point _e) {
     
		s = _s;
		e = _e;
	}
	
	// 线段相交判断
	// 2 规范相交
	// 1 非规范相交
	// 0 不相交
	int segcrossseg(Line v) {
     
		int d1 = sgn((e - s) ^ (v.s - s));
		int d2 = sgn((e - s) ^ (v.e - s));
		int d3 = sgn((v.e - v.s) ^ (s - v.s));
		int d4 = sgn((v.e - v.s) ^ (e - v.s));
		if ((d1 ^d2) == -2 && (d3 ^ d4) == -2) return 2;
		return (d1 == 0 && sgn((v.s - s)*(v.s - e)) <= 0) ||
			(d2 == 0 && sgn((v.e - s) *(v.e - e)) <= 0) ||
			(d3 == 0 && sgn((s - v.s) *(s - v.e)) <= 0) ||
			(d4 == 0 && sgn((e - v.s) *(e - v.e)) <= 0);
	}
};

Point p[N];
Line L[N];
double dis[N][N];

int main()
{
     
	int n;
	while (scanf("%d", &n) && n != -1) {
     
		for (int i = 0; i <= 4 * n + 1; ++i)
			for (int j = 0; j <= 4 * n + 1; ++j)
				dis[i][j] = inf;

		p[0] = Point(0, 5);
		double x, d1, m1, m2, u1;
		int lcnt = 0, pcnt = 0;
		for (int i = 1; i <= n; ++i) {
     
			scanf("%lf%lf%lf%lf%lf", &x, &d1, &m1, &m2, &u1);
			L[++lcnt] = Line(Point(x, 0), Point(x, d1));
			L[++lcnt] = Line(Point(x, m1), Point(x, m2));
			L[++lcnt] = Line(Point(x, u1), Point(x, 10));
			p[++pcnt] = Point(x, d1);
			p[++pcnt] = Point(x, m1);
			p[++pcnt] = Point(x, m2);
			p[++pcnt] = Point(x, u1);
		}
		p[++pcnt] = Point(10, 5);
		for (int i = 0; i <= pcnt; ++i) {
     
			for (int j = 0; j <= pcnt; ++j) {
     
				if (i != j) {
     
					Line L1(p[i], p[j]);
					bool ok = true;
					for (int k = 1; k <= lcnt; ++k) {
     
						if (L1.segcrossseg(L[k]) == 2) {
     
							ok = false;
							break;
						}
					}
					if (ok) dis[i][j] = p[i].distance(p[j]);
				}
			}
		}
		for (int k = 0; k <= pcnt; ++k) {
     
			for (int i = 0; i <= pcnt; ++i) {
     
				for (int j = 0; j <= pcnt; ++j) {
     
					if (dis[i][k] + dis[k][j] < dis[i][j]) {
     
						dis[i][j] = dis[i][k] + dis[k][j];
					}
				}
			}
		}
		printf("%.2f\n", dis[0][pcnt]);
	}
	return 0;
}

Pick-up sticks
寻找最上面的木棍编号。
一不小心就会TLE,需要先存储所有线段,然后判断当前线段有没有被之后的线段覆盖,如果覆盖就break,然后记录线段编号用于优化常数
计算几何总结点、线段、直线_第6张图片

#include 
#include 
#include 
#include 
#include 
#include 
#pragma warning(disable:4996)

using namespace std;

const double eps = 1e-8;
const int N = 1e5 + 5;

int sgn(double x) {
     
	if (fabs(x) < eps) return 0;
	if (x < 0) return -1;
	else return 1;
}

struct Point {
     
	double x, y;
	Point() {
     }
	Point(double _x, double _y) {
     
		x = _x;
		y = _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;
	}
	double operator * (const Point &b) const {
     
		return x * b.x + y * b.y;
	}
};

struct Line {
     
	Point s, e;
	Line() {
     }
	Line(Point _s, Point _e) {
     
		s = _s;
		e = _e;
	}
	// 两线段相交判断
	// 2规范相交
	// 1非规范相交
	// 0不相交
	int segcrossseg(Line v) {
     
		int d1 = sgn((e - s) ^ (v.s - s));
		int d2 = sgn((e - s) ^ (v.e - s));
		int d3 = sgn((v.e - v.s) ^ (s - v.s));
		int d4 = sgn((v.e - v.s) ^ (e - v.s));
		if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
		return (d1 == 0 && sgn((v.s - s)*(v.s - e)) <= 0) ||
			(d2 == 0 && sgn((v.e - s)*(v.e - e)) <= 0) ||
			(d3 == 0 && sgn((s - v.s)*(s - v.e)) <= 0) ||
			(d4 == 0 && sgn((e - v.s)*(e - v.e)) <= 0);
	}
};

Line L[N];
bool st[N];
int ans[N];

int main()
{
     
	int n;
	while (scanf("%d", &n) && n) {
     
		memset(st, false, sizeof(st));
		double x1, y1, x2, y2;
		for (int i = 1; i <= n; ++i) {
     
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			L[i] = Line(Point(x1, y1), Point(x2, y2));
		}
		int tot = 0;
		for (int i = 1; i <= n; ++i) {
     
			for (int j = i + 1; j <= n; ++j) {
     
				if (L[i].segcrossseg(L[j])) {
     
					st[i] = true;
					break;
				}
			}
			if (st[i] == false) ans[++tot] = i;
		}

		printf("Top sticks: ");
		for (int i = 1; i < tot; ++i)
			printf("%d, ", ans[i]);
		printf("%d.\n", ans[tot]);
	}
	system("pause");
	return 0;
}

极角排序

确定极点后,利用叉积按照顺时针或逆时针的方向对点进行排序

题目

Space Ant
蚂蚁的行动有三个限制:
1)不能右转
2)走过的路会留下红色的标记
3)不会经过之前走过的留下红色标记的路
计算几何总结点、线段、直线_第7张图片


#include 
#include 
#include 
#include 
#include 
#include 
#pragma warning(disable:4996)

using namespace std;

const double eps = 1e-8;
const int maxp = 55;

int sgn(double x) {
     
	if (fabs(x) <= eps) return 0;
	else if (x < 0) return -1;
	else return 1;
}

struct Point {
     
	double x, y;
	int id;
	Point() {
     }
	Point(double _x, double _y) {
     
		x = _x;
		y = _y;
	}
	bool operator < (Point b) const {
     
		return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : 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;
	}
	double operator *(const Point &b) const {
     
		return x * b.x + y * b.y;
	}
	double distance(Point p) {
     
		return hypot(x - p.x, y - p.y);
	}
};

Point p[maxp];

struct cmp {
     
	Point p;
	cmp(const Point &p0) {
      p = p0; }
	bool operator()(const Point &aa, const Point &bb) {
     
		Point a = aa, b = bb;
		int d = sgn((a - p) ^ (b - p));
		if (d == 0) {
     
			return sgn(a.distance(p) - b.distance(p)) < 0;
		}
		return d > 0;
	}
};

int ans[maxp];

int main()
{
     
	int T;
	scanf("%d", &T);
	while (T--) {
     
		int n, id;
		scanf("%d", &n);
		double x, y;
		for (int i = 0; i < n; ++i) {
     
			scanf("%d%lf%lf", &id, &x, &y);
			p[i] = Point(x, y);
			p[i].id = id;
		}
		Point mi = p[0];
		for (int i = 1; i < n; ++i) mi = min(mi, p[i]);
		for (int i = 0; i < n; ++i) {
     
			ans[i] = mi.id;
			sort(p + i, p + n, cmp(mi));
			mi = p[i + 1];
		}
		printf("%d", n);
		for (int i = 0; i < n; ++i) printf(" %d", ans[i]);
		printf("\n");
	}
	system("pause");
	return 0;
}

写在最后的一点感想

可能这是最后一篇关于ACM算法的博客了、明天就是ICPC济南站了,500多个队伍只有200多个牌子。写一点感想吧,还记得大一完的暑假,因为是转专业的,之前从没学过程序设计,想着暑假提前学点东西,看到ACM有暑期训练就加入了,第一次的测试赛,我连一道求和的签到题都写不出来,但是暑期训练结束的比赛好歹可以写三道题了,还拿了一个最快解题奖。后来厚着脸皮加了ACM队,找了wq和yzl组队,不得不说我是真的菜,也很感谢队友不嫌弃。但是训练的经历对于课程的学习帮助很大,短时间内提高一个人的程序设计能力,自学能力,debug能力等等,对于一个人的影响很长远。
打ACM一年半了,第一年太菜了,只有这半年参加了CCPC一次,ICPC一次都打铁了,还有明天的最后一场退役赛,我们能拿牌吗?我不知道(或许我知道,但还是想有希望)。常常怀疑我是不是选错了路,ACM是不是不适合我,也许是沉没成本,也许是学习算法的新知识,也许是和队友一起学习训练的感觉,让我一直坚持到了现在。
最近一直在想没有了ACM我应该做些什么呢?大二和大三上的课余时间大多都用来刷题了,不用刷题之后,我有什么事情可以做呢。搞科研项目?做数学建模?准备考研?准备保研(又很悬)?学习英语?重拾吉他?我不知道,感觉自己一直在忙碌又没有成果,只有看得过去的可怜绩点和没有含金量的几个比赛奖项。

明天会怎样呢?
计算几何总结点、线段、直线_第8张图片

你可能感兴趣的:(#,数学)