您的位置:首页 > 其它

POJ 3304 Segment 直线与线段相交 + 枚举

2013-08-29 10:34 281 查看
若存在这样一条直线,过投影相交区域作直线的垂线,该垂线必定与每条线段相交,问题转化为问是否存在一条线和所有线段相交;

若存在一条直线与所有线段相机相交,将该线旋转,平移,直到不能再动为止,此时该直线必定经过这些线段的某两个端点;

所以枚举任意两个端点即可。

 

需要注意的是,当枚举的两个端点很近的时候,即sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<eps时,那么这两个点是可以认为重合的,由于如果把这两点的连线作为向量,向量的模会很小,就可能导致无论怎么做叉积结果都为0从而认为各个点都是和这两个点共线的。因此,在枚举两个端点的时候要避免这种情况,同时,避免枚举这种情况之后也一定不会丢解。

 




View Code

#include<stdio.h>
#include<string.h>
#include<math.h>
#define eps 10e-8
struct point
{
double x, y;
};
struct segment
{
point s, t;
};
segment s[101];
int n;

double ff(double x)//平方
{
return x*x;
}
bool dis(point a, point b)//判断2个点是不是同一个点,本题很近的点就算是同一个点
{
double ret = sqrt(ff(a.x - b.x )+ff(a.y - b.y));
if(-eps < ret && ret < eps)return 0;
return 1;
}
double cross(point o, point a, point b)//叉积
{
return (a.x - o.x)*(b.y - o.y)- (a.y - o.y)*(b.x - o.x);
}
bool judge(point a, point b)//直线的2个点位a,b,判断这条直线与n条线段是否相交,都相交返回1,否则返回0.
{
if(!dis(a, b))return 0;
int i;
for(i=1;i<=n;i++)
if(cross(a, b, s[i].s)*cross(a, b, s[i].t) > eps)return 0;
return 1;
}
int main()
{
int cas,i ,j ;
scanf("%d",&cas);
while(cas--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%lf%lf%lf%lf", &s[i].s.x, &s[i].s.y, &s[i].t.x, &s[i].t.y);
bool ok = 0;
if(n==1)ok = 1;
for(i=1;i<n;i++)//枚举线段的端点
for(j=i+1;j<=n;j++)
if(judge(s[i].s, s[j].s)||judge(s[i].s, s[j].t)||judge(s[i].t, s[j].s)||judge(s[i].t, s[j].t)){ok=1;break;}
puts(ok ? "Yes!":"No!");
}
return 0;
}


 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: