POJ3304:直线与线段相交

POJ3304

题解

  • 所有线段的投影都交于一点,相当于有一条直线的垂线经过所有的线段。
  • 直线经过所有的点,我们可以旋转直线,使它恰好经过线段的两个端点,所有O(n^2)枚举端点。
  • 判断ab直线和线段cd是否相交,只要判ab×ac和ab×ad的符号是否小于等于0。比判断线段和线段相交少一个条件。
  • 最后注意重点的判断,因为叉积为0可能是直线和线段重合。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
double const eps = 1e-12;
int const N = 100 + 10;
int n;
typedef struct Point{
	double x,y;
	Point(){};
	Point(double x,double y):x(x),y(y){};
	Point operator - (const Point& e)const{
		return Point(x - e.x,y - e.y);
	}
}Vector;
struct Line{
	Point a,b;
}line[N];
int dcmp(double x){    //判断符号
	if(fabs(x) < eps)	return 0;
	else return x < 0 ? -1 : 1;
}
double cross(Vector a,Vector b){   //向量a×b
	return a.x * b.y - a.y * b.x;
}
bool Judge(Vector a,Vector b){   //直线ab
	if(dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0)	return false;  	//重点
	for(int i=0;i<n;i++)
		if(dcmp(cross(a - b,a - line[i].a)) * dcmp(cross(a - b,a - line[i].b)) > 0)	return false;
	return true;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%lf%lf%lf%lf",&line[i].a.x,&line[i].a.y,&line[i].b.x,&line[i].b.y);
		bool flag = false;
		for(int i=0;i<n && !flag;i++)
			for(int j=0;j<n && !flag;j++){    //端点两两组合有四种情况
				if(Judge(line[i].a,line[j].a) || Judge(line[i].a,line[j].b)
					||Judge(line[i].b,line[j].a) || Judge(line[i].b,line[j].b))
					flag = true;
			}
		if(flag)	printf("Yes!\n");
		else 	printf("No!\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42264485/article/details/88883492