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;
}
2.计算几何+并查集:O(N^2) + O(log(N)) Accept 721K / 47MS
#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;
}