您的位置:首页 > 其它

POJ 3304 Segments (判断直线和线段是否相交)

2014-02-10 16:48 120 查看
http://poj.org/problem?id=3304

思路:若所有线段在直线l上的投影有公共点p,那我们可以形象地说,(由投影的定义)必有一束细光穿过所有线段,投到直线l上,且在之上形成了一个光点p。

那么,是否有这么一束光呢?如何求呢?

方法:枚举两两线段的各一个端点,连一条直线,再判断剩下的线段是否都和这条直线有交点。

证明:为什么只需每次枚举两个端点就行了呢?首先假设这条直线不过任何端点,则我们可以左右平移这条直线,直到“脱离”了某个线段,此时与某个端点a相交;然后再将直线绕点a旋转,直到又“脱离”了一条线段,此时直线与另一端点相交,这就是我们需要的直线。证毕。

陷阱:可能有两条线段有一个端点重合,请跳过这种情况。

完整代码:

/*16ms,352KB*/

#include<cstdio>
#include<cmath>
const int mx = 105;
const double eps = 1e-8;

struct point
{
	double x, y;
	//point(double x = 0, double y = 0): x(x), y(y) {}
} p[2 * mx];

///计算p1p2 x p1p
double cross_product(point p1, point p2, point p)
{
	return (p2.x - p1.x) * (p.y - p1.y) - (p2.y - p1.y) * (p.x - p1.x);
}

bool judge(int n)
{
	int i, j, k;
	for (i = 0; i < n; ++i)
	{
		for (j = 0; j < n; ++j)
		{
			if (fabs(p[i].x - p[j].x) < eps && fabs(p[i].y - p[j].y) < eps) continue; ///两点重合
			for (k = 0; k < n; k += 2)
				if ( cross_product(p[i], p[j], p[k]) * cross_product(p[i], p[j], p[k + 1]) > 0 ) break;
                ///直线pi-pj与线段p1k-p2k不相交
			if (k == n) return true;
		}
	}
	return false;
}

int main()
{
	int t, n, i, j;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		n <<= 1;
		for (i = 0; i < n; ++i) scanf("%lf%lf", &p[i].x, &p[i].y);
		if (n <= 4) puts("Yes!");
		else puts(judge(n) ? "Yes!" : "No!");
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: