POJ 3304 Segments(直线与线段相交判定)

558 篇文章 0 订阅
273 篇文章 0 订阅

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值