计算几何
1. 判断线段是否相交
- 在不需求出交点,只需判断两条线段是否相交,可以使用
1.
快
速
排
斥
实
验
1.快速排斥实验
1.快速排斥实验 和
2.
跨
立
实
验
2.跨立实验
2.跨立实验 解决。
参考链接:https://blog.youkuaiyun.com/tengchongwei/article/details/72922056 - 需要求出交点的情况可以先判断是否平行。如果不平行,求直线
L
1
(
p
1
p
2
)
L1(p1p2)
L1(p1p2) 和 直线
L
2
(
q
1
q
2
)
L2(q1q2)
L2(q1q2) (其中
p
1
、
p
2
p1、p2
p1、p2 为直线
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)}
交点向量=p1+(p2−p1)∗(q2−q1)×(p2−p1)(q2−q1)×(q1−p1)即可求出两条直线的交点坐标,再检查交点是否同时在两条线段上就行了。
参考白书 : P250
试验题目
P
O
J
1127
POJ1127
POJ1127
题目链接: http://poj.org/problem?id=1127
题意:
n
n
n 根木棍,给出木棍坐标,询问两根木棍是否相交,间接连在一起也算相交。
分析: 这道题需要解决两个问题
- 判断两根木棍是否相交
- 间接相交如何判断
对于第一个问题,有上述两种方法解决。第二个问题可以使用并查集,但对于本题白书上给出了一种更好的解决方法— 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;
}