计算几何+并查集处理:POJ 1127 Jack Straws

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值