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

提示:


题意解析:

题意很好理解,两线段有一个交点 且 交点不是任意一条线段的端点时为 规范相交 。

具体实现:

首先最容易想到的方法是直接求交点,并判断交点位置,进一步判断是否为规范相交,由于线段上已知两点,故可以求出直线的斜截式方程并求出交点,步骤如下:

 

       在实际运行中,发现会遇到许多特殊情况会导致BUG,例如两条线段在同一条直线上,有一条线段与Y轴平行等,这些情况都需要特殊处理,会使代码冗杂且容易出错,故需要一种快速筛选特殊情况的算法。

第一种便是快速排斥,可以帮我们快速判断两条线段是否可能相交。原理是判断通过两条线段分别在X轴和Y轴上的投影是否有重叠部分,如果没有重叠,故两条线段不可能相交。

第二种是跨立实验,利用叉积,来判断其中一条线段的两端点是否在另一条线段的两侧。

AC代码:

#include<bits/stdc++.h>
using namespace std;

double ax,ay,bx,by,cx,cy,dx,dy,x,y;
double k1,k2,b1,b2;
double minx1,minx2,maxx1,maxx2,miny1,miny2,maxy1,maxy2;

bool contain()
{
	if(cx>=min(ax,bx) && cx<=max(ax,bx) && cy>=min(ay,by) && cy<=max(ay,by))
	return true;
	else
	return false;
}

bool cross()
{//求叉积 
	double j1,j2;
	j1=((cx - ax) * (dy - cy) - (cy - ay) * (dx - cx)) / ((bx - ax) * (dy - cy) - (dx - cx) * (by - ay));
	j2=((ax - cx) * (by - ay) + (cy - ay) * (bx - ax)) / ((dx - cx) * (by - ay) - (bx - ax) * (dy - cy));
	if(j1>=0 && j1<=1 && j2>=0 && j2<=1)
	{//利用叉积求交点 
		x=ax+j1*(bx-ax);
        y=ay+j1*(by-ay);
        return true;
    }
	else return false;
}
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&ax,&ay,&bx,&by,&cx,&cy,&dx,&dy);
		
		//利用直线的斜截式求交点 
		k1=(bx-ax)/(by-ay);
		b1=ay-(ax*(ay-by))/(ax-bx);
		k2=(dx-cx)/(dy-cy);
		b2=cy-(cx*(cy-dy))/(cx-dx);

		//求线段边界 
		maxx1=max(ax,bx);
		minx1=min(ax,bx);
		maxx2=max(cx,dx);
		minx2=min(cx,dx);
		maxy1=max(ay,by);
		miny1=min(ay,by);
		maxy2=max(cy,dy);
		miny2=min(cy,dy);
		
		if (maxx1<minx2 || minx1>maxx2 || maxy1<miny2 || miny1>maxy2)
	    {//快速排斥 
	        printf("NO\n");
	        continue;
	    }
		
//		x=(b2-b1)/(k1-k2);
//		y=x*k1;
		
		if ((by-ay)*(dx-cx)==(bx-ax)*(dy-cy))
		{
			if((cy-ay)*(bx-ax)==(cx-ax)*(by-ay))
			{
				if(contain() )
				{
					printf("YES\n");
					continue;
				}
			}
			printf("NO\n");
			continue;
		}
		
		if (cross())
		{
			printf("YES");
			if((x != ax || y != ay) && (x != bx || y != by) && (x != cx || y != cy) && (x != dx || y != dy))
			{
				printf(" (%.3f,%.3f)",x,y);
			}
			printf("\n");
		}
		else printf("NO\n");
	}
	return 0;
}

总结:

快速排斥,叉积

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NsJhR

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

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

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

打赏作者

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

抵扣说明:

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

余额充值