POJ 3304 Segments(直线与线段相交判定)
http://poj.org/problem?id=3304
题意:
给出n条线段两个端点的坐标,问将所有线段投影到一条直线上,如果这些所有投影至少相交于一点就输出Yes!,否则输出No!。
分析:
如果存在这么一条直线,使得所有线段都在该直线上投影,且投影部分有公共点,那么就等于存在该直线的垂线,该垂线与每一条线段都相交了. 且你可以通过先平移该垂线,然后再旋转该垂线,使得该”垂线”(旋转之后就不能叫垂线了)与之前所有的线段中至少正好相交于两个端点.
其实上面的结论是充分必要的: 线段投影有公共部分充要条件存在一条由所有线段中的两个端点确定的直线,该直线与每一条线段都相交(可能交于端点).
那么接下来我们只要枚举两两端点对,看该两端点构成的直线是否与每条直线都相交即可.
注意如果两个点的x与y坐标之差都<=1e-8,那么这两个点就应看成一个点.且精度值eps也应取1e-8.
AC代码:
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=100+10;
const double eps=1e-8;
int dcmp(double x)
{
if(fabs(x)<eps) return 0;
return x<0?-1:1;
}
struct Point
{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
};
typedef Point Vector;
Vector operator-(Point A,Point B)
{
return Vector(A.x-B.x,A.y-B.y);
}
bool operator==(Point A,Point B)
{
return dcmp(A.x-B.x)==0 && dcmp(A.y-B.y)==0;
}
double Cross(Vector A,Vector B)
{
return A.x*B.y-A.y*B.x;
}
bool LineInterSegment(Point a1,Point a2,Point b1,Point b2)//判断直线a1a2是否与线段b1b2相交(端点也算)
{
double c1=Cross(a1-a2,b1-a2),c2=Cross(a1-a2,b2-a2);
return dcmp(c1)*dcmp(c2)<=0;
}
struct Segment
{
Point a,b;
}S[maxn];
int n;//线段数
int num;//端点总数
Point P[maxn*2];//记录所有端点
bool check()
{
for(int i=0;i<num;++i)
for(int j=i+1;j<num;++j)
{
if(P[i]==P[j]) continue;//此句不加就WA
int k;
for(k=0;k<n;++k)
{
if(!LineInterSegment(P[i],P[j],S[k].a,S[k].b)) break;
}
if(k>=n) return true;
}
return false;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
num=0; //记录所有端点数
scanf("%d",&n);
for(int i=0;i<n;++i)
{
scanf("%lf%lf%lf%lf",&S[i].a.x,&S[i].a.y,&S[i].b.x,&S[i].b.y);
P[num++]=S[i].a;
P[num++]=S[i].b;
}
printf("%s\n",check()?"Yes!":"No!");
}
return 0;
}