POJ 1127题目大意如下:
有n根小棍分布在一个二维平面上,每根小棍标记了它两端的坐标,小棍的序号是从1~n,现在的问题是:给出任意两根小棍的序号,问它们是否是相连的。
这里相连的定义是:可以直接相连(相交),或者通过若干根其他小棍间接相连。
不能直接去研究两根小棍的相交情况(比如直接去求它们的交点坐标),可以考虑向量。
在向量里面,假设向量P1 = S1 - T1,P2 = S2 - T2(所有变量都是向量),那么考虑它们是否相交就等于(先不考虑平行):
1)假设两向量代表的直线相交点为Q,这时候假设Q对应的向量为Q,那么对于向量P1与Q的关系是:Q = T1 + (S1 - T1)*a(a是一个系数);
2)对于向量P2,从叉乘的角度:(S2 - T2)X(Q - T2) = 0;
3)联立以上两个方程,可以求出a = (T2 - S2)X(T1 - T2) / [(S2 - T2)X(S1 - T1)],同时Q的坐标也出来了;
4)讨论Q是否在P1和P2代表的线段上面,还是用向量,这时候如果Q在线段内的话(包括两个端点),就有:(Q - S1)*(Q - T1) <= 0 && (Q - S2)*(Q - T2) <= 0;
接下来讨论平行的情况:
如果两个向量平行同时要讨论它们是不是相包含(就是有重合的一部分),其实就是论证某个线段的某个端点是否在另一条线段之内,于是会有四种情况,这里省略论述。
在这些处理之后就已经知道直接相连的木棍了,接下来要考虑间接相连的木棍其实就是一个传递闭包问题,可以用Warshall算法或者并查集。
1.计算几何+Warshall,复杂度O(N^3) Accept 712K / 79MS
#include <iostream> #include <algorithm> #include <cstdio> #include <cmath> using namespace std; const int maxn = 15; const double EPS = 1e-10; int n; bool grap[maxn][maxn]; //精度处理 double add(double a, double b) { if (abs(a + b) < EPS*(abs(a) + abs(b))) return 0; return a + b; } struct Point { double x, y; Point(){}; Point(double x, double y): x(x), y(y){} Point operator + (Point p) { return Point(add(x, p.x), add(y, p.y)); } Point operator - (Point p) { return Point(add(x, -p.x), add(y, -p.y)); } Point operator * (double p) { return Point(x*p, y*p); } //点乘 double dot(Point p) { return add(x*p.x, y*p.y); } //叉乘 double det(Point p) { return add(x*p.y, -y*p.x); } }; Point s[maxn], t[maxn]; //点q是否在线段st内 bool on_seg(Point& s, Point& t, Point& q) { return (s - q).det(t - q) == 0 && (q - s).dot(q - t) <= 0; } //求交点 Point intersection(Point& s1, Point& t1, Point& s2, Point& t2) { return t1 + (s1 - t1)*((t2 - s2).det(t1 - t2) / (s2 - t2).det(s1 - t1)); } void init() { for (int i = 1; i <= n; i++) { grap[i][i] = true; for (int j = 1; j < i; j++) { if ((s[i] - t[i]).det(s[j] - t[j]) == 0) { grap[i][j] = grap[j][i] = on_seg(s[i], t[i], s[j]) || on_seg(s[i], t[i], t[j]) || on_seg(s[j], t[j], s[i]) || on_seg(s[j], t[j], t[i]); } else { Point temp = intersection(s[i], t[i], s[j], t[j]); grap[i][j] = grap[j][i] = on_seg(s[i], t[i], temp) && on_seg(s[j], t[j], temp); } } } //Warshall Algorithm for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { grap[i][j] |= grap[i][k] && grap[k][j]; } } } } int main() { int a, b; while (scanf("%d", &n) && n != 0) { for (int i = 1; i <= n; i++) scanf("%lf %lf %lf %lf", &s[i].x, &s[i].y, &t[i].x, &t[i].y); init(); while (scanf("%d %d", &a, &b) && a*b != 0) printf(grap[a][b] ? "CONNECTED\n" : "NOT CONNECTED\n"); } return 0; }
#include <iostream> #include <algorithm> #include <cstdio> #include <cmath> #include <cstring> using namespace std; const int maxn = 15; const double EPS = 1e-10; int n; int flag[maxn];//记录当前点所在的树的根下标 int Rank[maxn];//记录并查集树的高度 bool grap[maxn][maxn]; double add(double a, double b) { if (abs(a + b) < EPS*(abs(a) + abs(b))) return 0; return a + b; } struct Point { double x, y; Point(){}; Point(double x, double y): x(x), y(y){} Point operator + (Point p) { return Point(add(x, p.x), add(y, p.y)); } Point operator - (Point p) { return Point(add(x, -p.x), add(y, -p.y)); } Point operator * (double p) { return Point(x*p, y*p); } double dot(Point p) { return add(x*p.x, y*p.y); } double det(Point p) { return add(x*p.y, -y*p.x); } }; Point s[maxn], t[maxn]; bool on_seg(Point& s, Point& t, Point& q) { return (s - q).det(t - q) == 0 && (q - s).dot(q - t) <= 0; } Point intersection(Point& s1, Point& t1, Point& s2, Point& t2) { return t1 + (s1 - t1)*((t2 - s2).det(t1 - t2) / (s2 - t2).det(s1 - t1)); } //查找树的根 int findRoot(int x) { if (flag[x] == x) return x; return flag[x] = findRoot(flag[x]); } //连接两个点(树) void unite(int x, int y) { x = findRoot(x); y = findRoot(y); if (x == y) return; if (Rank[x] < Rank[y]) flag[x] = flag[y]; else { flag[y] = flag[x]; if (Rank[x] == Rank[y]) Rank[x]++; } } void init() { for (int i = 1; i <= n; i++) flag[i] = i; for (int i = 1; i <= n; i++) { grap[i][i] = true; for (int j = 1; j < i; j++) { if ((s[i] - t[i]).det(s[j] - t[j]) == 0) { grap[i][j] = grap[j][i] = on_seg(s[i], t[i], s[j]) || on_seg(s[i], t[i], t[j]) || on_seg(s[j], t[j], s[i]) || on_seg(s[j], t[j], t[i]); } else { Point temp = intersection(s[i], t[i], s[j], t[j]); grap[i][j] = grap[j][i] = on_seg(s[i], t[i], temp) && on_seg(s[j], t[j], temp); } if (grap[i][j]) unite(i, j); } } } int main() { int a, b; memset(Rank, 0, sizeof(Rank)); while (scanf("%d", &n) && n != 0) { for (int i = 1; i <= n; i++) scanf("%lf %lf %lf %lf", &s[i].x, &s[i].y, &t[i].x, &t[i].y); init(); while (scanf("%d %d", &a, &b) && a*b != 0) printf(findRoot(a) == findRoot(b) ? "CONNECTED\n" : "NOT CONNECTED\n"); } return 0; }