TZOJ——3348:线段相交Ⅲ(计算几何)

描述:

线段相交有两种情形:一种是“规范相交”,另一种是“非规范相交”。规范相交是指两条线段恰有唯一一个不是端点的公共点。即如果一条线段的端点在另一条线段上则不视为相交。如果两条线段有部分重合,也不视为相交。而非规范相交则把以上两种情况都视为相交。如下图所示:

规范相交认为a,b两种情况都是不相交的,而非规范相交认为a,b两种情况都是相交的。

本题要求判断两条线段是否相交。如果是规范相交则输出YES,并输出交点坐标,如果是非规范相交则只需输出YES,如果不相交则输出NO。

输入:

输入有多组数据,T表示输入数据的组数。每组测试数据有两行第一行输入一条线段的两个端点的坐标,第二行输入另一个线段的两个端点的坐标。

输出:

对于每组测试数据,输出一行。如果是规范相交则输出YES,并输出交点坐标(小数点后面保留3位),如果是非规范相交则只需输出YES,如果不相交则输出NO。

样例输入:

4
0 0 1 1
0 1 1 0
0 0 2 2
2 2 3 3
0 0 2 2
1.5 1.5 3 3
0 0 1 1
2 2 3 3
样例输出:

YES (0.500,0.500)
YES
YES
NO


题目分析:两条线段首先有两种情况:平行或不平行。如果平行,再判断两条线段的端点是否在另一条端点上,如果在则共线,即非规范相交,不在就只是平行;如果不平行,此时分为两种情况,因为是线段,所以有可能相交,有可能只是相离;相交还有一种特殊情况,就是线段1的端点在线段2上,此时也为非规范相交。

相交时可以先把两条线段所在直线的方程写出来,再进行联立,即可得出两个斜率的表达公式。

代码示例:

#include<iostream>
using namespace std;
struct point{
    double x,y;
}ans;
bool judge1(point a,point b)    //判断两个点是否相等,因为精度问题,所以尽量避开用=
{
    return a.x!=b.x||a.y!=b.y;
}
bool judge2(point a1,point a2,point p)
{
    if((p.x-a1.x)*(a2.y-a1.y)==(a2.x-a1.x)*(p.y-a1.y)   //叉积相减是否为零,这里改为判断左右两式是否相等
        &&min(a1.x,a2.x)<=p.x&&p.x<=max(a1.x,a2.x)  //判断p是否在线段a1a2上
        &&min(a1.y,a2.y)<=p.y&&p.y<=max(a1.y,a2.y))
        return true;
    return false;
}
int judge3(point a1,point a2,point b1,point b2)
{
    if((a2.y-a1.y)*(b2.x-b1.x)!=(b2.y-b1.y)*(a2.x-a1.x))    //叉积
        return 0;
    if(judge2(a1,a2,b1)||judge2(a1,a2,b2)||judge2(b1,b2,a1)||judge2(b1,b2,a2))
        return 1;
    return 2;
}
int judge4(point a,point b,point c,point d)
{
    if(!(min(a.x,b.x)<=max(c.x,d.x)&&min(c.x,d.x)<=max(a.x,b.x)
        &&min(a.y,b.y)<=max(c.y,d.y)&&min(c.y,d.y)<=max(a.y,b.y))) 
        return false;
    double k1=((c.x-a.x)*(d.y-c.y)-(c.y-a.y)*(d.x-c.x))/((b.x-a.x)*(d.y-c.y)-(d.x-c.x)*(b.y-a.y));  //斜率1
    double k2=((a.x-c.x)*(b.y-a.y)+(c.y-a.y)*(b.x-a.x))/((d.x-c.x)*(b.y-a.y)-(b.x-a.x)*(d.y-c.y));  //斜率2
    if(k1>=0&&k1<=1&&k2>=0&&k2<=1)
    {
        ans.x=a.x+k1*(b.x-a.x);
        ans.y=a.y+k1*(b.y-a.y);
        return true;
    }
    return false; 
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        point a1,a2,b1,b2;
        scanf("%lf%lf%lf%lf",&a1.x,&a1.y,&a2.x,&a2.y);
        scanf("%lf%lf%lf%lf",&b1.x,&b1.y,&b2.x,&b2.y);
        if(judge3(a1,a2,b1,b2)==1)
            puts("YES");
        else if(judge3(a1,a2,b1,b2)==2)
            puts("NO");
        else
        {
            if(judge4(a1,a2,b1,b2))
            {
                printf("YES");
                if(judge1(ans,a1)&&judge1(ans,a2)&&judge1(ans,b1)&&judge1(ans,b2))
                    printf(" (%.3f,%.3f)",ans.x,ans.y);
                puts("");
            }
            else
                puts("NO");
        }
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HYoung:)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值