题目链接:https://vjudge.net/contest/368031#problem/C
题目描述:
经过激烈的争夺,Lele终于把那块地从Yueyue的手里抢了回来。接下来,Lele要开始建造他的灌溉系统。
通过咨询Lele的好友——化学系的TT,Lele决定在田里挖出N条沟渠,每条沟渠输送一种肥料。
每条沟渠可以看作是一条折线,也就是一系列线段首尾连接而成(除了第一条线段开头和最后一条线段的结尾)。由于沟渠很细,你可以忽略掉它的宽度。
由于不同的肥料之间混合会发生化学反应,所以修建的沟渠与沟渠之间不能相交。
现在TT给Lele画了一些设计图,Lele请你判断一下设计图中的沟渠与沟渠之间是否有相交。
Input
本题目包含多组测试,请处理到文件结束(EOF)。
每组测试的第一行有一个正整数N(0<N<30),表示管道的数目。接下来给出这N条管道的信息。
对于每条管道,第一行是一个正整数K(0<K<100),表示这条管道是由K个端点组成。
接下来的K行给出这K个端点信息。每个端点占一行,用两个整数X和Y(0<X,Y<1000)分别表示这个端点的横坐标和纵坐标的值。
Output
对于每组测试,如果该测试管道与管道之间有相交的话,输出"Yes",否则输出"No"。
Sample Input
2
2
0 0
1 1
2
0 1
1 0
2
2
0 0
1 1
2
1 0
2 1
2
3
0 0
1 1
2 1
2
2 0
3 0
Sample Output
Yes
No
No
输入输入解析:
输入:
2
2
0 0
1 1
2
0 1
1 0
输出:
Yes
第一个2表示有两条折线。接下来描述这n条折线的信息。第二行的2表示第一条折线有2个点组成。
接下来的两行表示这两个点的横纵坐标。接着输入下一个折线的点的个数和对应的坐标。
分析:
每一条折线有若干点,枚举组成每一条折线的线段和其余折线的所有线段看是否相交。
知识点:
外积,又称叉积。两个向量v1(x1, y1)和v2(x2, y2)的外积v1×v2=x1y2-y1x2。
根据右手法则,如果由v1到v2是顺时针转动,外积为负,反之为正;为0表示二者方向相同(平行)。
考虑如何考虑判断两条线段相交?
若AB和CD相交需满足C和D分别在AB的两侧且A和B分别在CD的两侧。
问题转化为如何证明两点分别在一条线的两侧? 例如证明 C和D分别在AB的两侧?
引入向量AC和AD
上图是AB和CD相交的情况,可以看出向量AB×AD < 0,AB×AC > 0。
可以得出CD的2个端点C和D,与另一条线段的一个端点(A或B)连成的向量,与向量AB做叉乘,结果异号则C和D分别在AB的两端。
特殊情况:
AB和CD相交于点C,此时AB×AC=0,AB×AD <0。此时一个负的,一个为0,可以看出此时两条线段也是相交的。可以得出有一方叉乘的结果为0可以看成两条线段也是相交的。
AB和CD的叉乘为0(即AB和CD平行):
可以看出:
在平行的前提下:可以分为共线和不共线两种情况
那如何判断共线?
我们可以在两条线段中各取一点,用这两点组成的向量与其中一条线段进行叉乘,结果若为0,就表示两线段共线。
可是共线却不一定相交,例如上图的第二种情况。
那如何判断相交?
解决方法:
我们给每条线段的两个端点排序:横坐标大的点更大,横坐标相同,纵坐标大的点更大。
排好序后,每条线段中,小的点当起点,大的当终点。
若一条线段AB与另一条线段CD共线,且线段AB的起点小于等于线段CD的起点,但线段AB的终点大于等于线段CD的起点。则两条线段相交。
代码:
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,flag;
int k[40];
struct node
{
int x,y;
} s[40][110];
double f(node a,node b,node c)///如果AB.AC,得到的结果是垂直于ABC这个平面的一个向量
{
///两个向量的向量积,是一个垂直于两个向量的向量
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);///AB.AC=(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int dfs(node a,node b,node c,node d)///组成两条线段的四个点
{
if(max(a.x,b.x)>=min(c.x,d.x)&&min(c.x,d.x)<=max(a.x,b.x)&&
max(a.y,b.y)>=min(c.y,d.y)&&min(c.y,d.y)<=max(a.y,b.y)&&
f(a,b,c)*f(a,b,d)<=0&&f(d,c,a)*f(d,c,b)<=0)///ab×ac * ab×ad
return 1;
return 0;
}
int main()
{
while(~scanf("%d",&n))
{
memset(k,0,sizeof(k));
flag=0;
for(int i=0; i<n; i++)
{
scanf("%d",&k[i]);
for(int j=0; j<k[i]; j++)
scanf("%d%d",&s[i][j].x,&s[i][j].y);
}
if(n==1)
{
printf("No\n");
continue;
}
for(int i=0; i<n-1; i++)///枚举每一条线段
for(int j=1; j<k[i]; j++)///这条线段的所有点
for(int u=i+1; u<n; u++)
for(int v=1; v<k[u]; v++)
if(dfs(s[i][j-1],s[i][j],s[u][v-1],s[u][v]))
{
flag=1;
break;
}
if(flag)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}