计算几何 (POJ1127 、 )

25 篇文章 0 订阅
16 篇文章 0 订阅

1. 判断线段是否相交

  • 在不需求出交点,只需判断两条线段是否相交,可以使用 1. 快 速 排 斥 实 验 1.快速排斥实验 1. 2. 跨 立 实 验 2.跨立实验 2. 解决。
    参考链接:https://blog.csdn.net/tengchongwei/article/details/72922056
  • 需要求出交点的情况可以先判断是否平行。如果不平行,求直线 L 1 ( p 1 p 2 ) L1(p1p2) L1p1p2 和 直线 L 2 ( q 1 q 2 ) L2(q1q2) L2q1q2 (其中 p 1 、 p 2 p1、p2 p1p2 为直线 L 1 L1 L1 的端点) 交点就利用公式 交 点 向 量 = p ⃗ 1 + ( p ⃗ 2 − p ⃗ 1 ) ∗ ( q ⃗ 2 − q ⃗ 1 ) × ( q ⃗ 1 − p ⃗ 1 ) ( q ⃗ 2 − q ⃗ 1 ) × ( p ⃗ 2 − p ⃗ 1 ) 交点向量 = \vec p_1 + (\vec p_2 - \vec p_1) * \frac {(\vec q_2- \vec q_1) \times (\vec q_1 - \vec p_1)} {(\vec q_2- \vec q_1) \times (\vec p_2 - \vec p_1)} =p 1+(p 2p 1)(q 2q 1)×(p 2p 1)(q 2q 1)×(q 1p 1)即可求出两条直线的交点坐标,再检查交点是否同时在两条线段上就行了。
    参考白书 : P250

试验题目 P O J 1127 POJ1127 POJ1127
题目链接: http://poj.org/problem?id=1127

题意: n n n 根木棍,给出木棍坐标,询问两根木棍是否相交,间接连在一起也算相交。
分析: 这道题需要解决两个问题

  1. 判断两根木棍是否相交
  2. 间接相交如何判断

对于第一个问题,有上述两种方法解决。第二个问题可以使用并查集,但对于本题白书上给出了一种更好的解决方法— F l o y d 算 法 Floyd算法 Floyd ,将每个棍看成是一个点,相交的木棍间有边。那么点 i i i 和点 k k k 相连通,并且点 k k k 和点 j j j 相连通,则 i i i j j j 相连通,即 i i i j j j 木棍相交。

第一种方法 : 1. 快 速 排 斥 实 验 1.快速排斥实验 1. + 2. 跨 立 实 验 2.跨立实验 2.

// 不需求出交点,只需判断两条线段是否相交
#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
const double EPS = 1e-10;
const int MAXN = 300;

double add(double a, double b) {
	if(abs(a+b) < EPS*(abs(a) + abs(b))) return 0;
	return a+b;
}

// 二维向量结构体
struct P {
	double x, y;
	P() {}
	P(double x, double y) : x(x), y(y) {}
	P operator + (P p) {
		return P(add(x, p.x), add(y, p.y));
	}
	P operator - (P p) {
		return P(add(x, -p.x), add(y, -p.y));
	}
	P operator * (double d) {
		return P(x*d, y*d);
	}	
	// 内积,点乘
	double dot(P p) {
		return add(x*p.x, y*p.y);
	} 
	// 外积,叉乘
	double det(P p) {
		return add(x*p.y, -y*p.x);
	}
};

struct Line {
	P p1, p2;
};

// 不需求出交点,只需判断两条线段是否相交
bool seg_inter(Line L1, Line L2) {
	// 快速排斥实验
	if( min(L1.p1.x, L1.p2.x) > max(L2.p1.x, L2.p2.x) ||
		min(L2.p1.x, L2.p2.x) > max(L1.p1.x, L1.p2.x) ||
		min(L1.p1.y, L1.p2.y) > max(L2.p1.y, L2.p2.y) ||
		min(L2.p1.y, L2.p2.y) > max(L1.p1.y, L1.p2.y) ) return false;
	// 跨立实验
	if( (L1.p1-L2.p1).det(L2.p2-L2.p1) * (L1.p2-L2.p1).det(L2.p2-L2.p1) <= 0 &&
		(L2.p1-L1.p1).det(L1.p2-L1.p1) * (L2.p2-L1.p1).det(L1.p2-L1.p1) <= 0 ) return true;
	else return false;
}

int n, l, r;
bool g[MAXN][MAXN];
Line L[MAXN];
void solve() {
	for (int i = 0; i < n; ++i) {
		g[i][i] = 1;
		for (int j = 0; j < i; ++j) {
			g[i][j] = g[j][i] = seg_inter(L[i], L[j]);
		}
	}
	// Floyd 判断任意两点间是否相连
	for (int k = 0; k < n; ++k) {
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				g[i][j] |= (g[i][k] & g[k][j]);
			}
		}
	}
}

int main(int argc, char const *argv[])
{
	while(scanf("%d", &n) && n != 0) {
		for (int i = 0; i < n; ++i) {
            // cin>>L[i].p1.x>>L[i].p1.y>>L[i].p2.x>>L[i].p2.y;
			// scanf 对输入严格按照地址,非常严格。
			scanf("%lf%lf%lf%lf", &L[i].p1.x, &L[i].p1.y, &L[i].p2.x, &L[i].p2.y);
		}		
		solve();
		while(scanf("%d%d", &l, &r)) {
			if(l == 0 && r == 0) break;
			puts(g[l-1][r-1] ? "CONNECTED" : "NOT CONNECTED");
		}
	}
	return 0;
}

第二种方法: 求出交点,判断是否在两条线段上。

// 能求出交点
#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
const double EPS = 1e-10;
const int MAXN = 300;

double add(double a, double b) {
	if(abs(a+b) < EPS*(abs(a) + abs(b))) return 0;
	return a+b;
}

// 二维向量结构体
struct P {
	double x, y;
	P() {}
	P(double x, double y) : x(x), y(y) {}
	P operator + (P p) {
		return P(add(x, p.x), add(y, p.y));
	}
	P operator - (P p) {
		return P(add(x, -p.x), add(y, -p.y));
	}
	P operator * (double d) {
		return P(x*d, y*d);
	}	
	// 内积,点乘
	double dot(P p) {
		return add(x*p.x, y*p.y);
	} 
	// 外积,叉乘
	double det(P p) {
		return add(x*p.y, -y*p.x);
	}
};

struct Line {
	P p1, p2;
};

// 判断点 q 是否在线段 p1-p2 上
bool on_seg(Line L, P q) {
	return (L.p1-q).det(L.p2-q) == 0 && (L.p1-q).dot(L.p2-q) <= 0;
}

// 计算直线 p1-p2 与 直线 q1-q2 的交点
P intersec(Line L1, Line L2) {
	return L1.p1 + (L1.p2-L1.p1) * ((L2.p2-L2.p1).det(L2.p1-L1.p1) / (L2.p2-L2.p1).det(L1.p2-L1.p1));
}

int n;
Line L[MAXN];
bool g[MAXN][MAXN];
void solve() {
	for (int i = 0; i < n; ++i) {
		g[i][i] = 1;
		for (int j = 0; j < i; ++j) {
			// 判断木棍 i 和 j 是否有公共点
			if((L[i].p2-L[i].p1).det(L[j].p2-L[j].p1) == 0) {
				// 平行
				g[i][j] = g[j][i] = on_seg(L[i], L[j].p1) 
								 || on_seg(L[i], L[j].p2)
								 || on_seg(L[j], L[i].p1)
								 || on_seg(L[j], L[i].p2);
			}
			else {
				// 不平行
				P r = intersec(L[i], L[j]);
				g[i][j] = g[j][i] = on_seg(L[i], r) && on_seg(L[j], r);
			}
		}
	}

	// Floyd 判断任意两点间是否相连
	for (int k = 0; k < n; ++k) {
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				g[i][j] |= (g[i][k] & g[k][j]);
			}
		}
	}
}

int main(int argc, char const *argv[])
{
	while(scanf("%d", &n) && n != 0) {
		for (int i = 0; i < n; ++i) {
			scanf("%lf%lf%lf%lf", &L[i].p1.x, &L[i].p1.y, &L[i].p2.x, &L[i].p2.y);
		}	
		solve();
		int l, r;
		while(scanf("%d%d", &l, &r)) {
			if(l == 0 && r == 0) break;
			puts(g[l-1][r-1] ? "CONNECTED" : "NOT CONNECTED");
		}	
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值