题目链接
题意:
给你若干条线段,求他们是否能投影到一条直线上,使得每条线段的投影至少有一个公共交点。
题解:
我们过那个公共交点作那条直线的垂线,根据投影的性质,不难发现这条直线应该与所有线段有交点。那么就转化为了判断是否有一条直线与所有的线段都有交点就行了。那么现在的问题是该如何找直线呢。稍微思考一下,我们会发现好像每条线段只有两个端点是有用的,对于所有合法的直线,最边界的合法情况就是经过了某条线段的一个端点,于是我们枚举线段端点,用两个线段端点连形组成的直线来判断是否与每条线段都有交即可。判断的方法是用叉积,利用叉积的结果是根据两向量的位置关系有正有负的。选择两个端点中的一个作为向量的起点,另一个端点作为终点,同时其他所有线段的两个端点分别作为终点作叉积,如果对于两个端点的叉积一正一反,则说明该线段的两个端点分别在当前直线两侧,也就是有交点,当然叉积为0重合也是有交点。这题就这样做完了。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
using namespace std;
int t,n,pd;
double eps=1e-8;
struct Point
{
double x,y;
};
int cmp(double x)
{
if(fabs(x)<eps)
return 0;
if(x>0)
return 1;
else
return -1;
}
bool operator != (Point x,Point y)
{
return (cmp(x.x-y.x)!=0||cmp(x.y-y.y)!=0);
}
struct Vector
{
double x,y;
};
struct Line
{
Point p,q;
}l[110];
double chaji(Vector x,Vector y)
{
return x.x*y.y-x.y*y.x;
}
int check(Point x,Point y)
{
Vector v1;
v1.x=x.x-y.x;
v1.y=x.y-y.y;
for(int i=1;i<=n;++i)
{
Point b=l[i].p,c=l[i].q;
Vector v2,v3;
v2.x=b.x-y.x;
v2.y=b.y-y.y;
v3.x=c.x-y.x;
v3.y=c.y-y.y;
int ji1,ji2;
ji1=cmp(chaji(v1,v2));
ji2=cmp(chaji(v1,v3));
if(ji1!=ji2||!ji1)
continue;
else
return 0;
}
return 1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
pd=0;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lf%lf%lf%lf",&l[i].p.x,&l[i].p.y,&l[i].q.x,&l[i].q.y);
for(int i=1;i<=n;++i)
{
for(int j=i;j<=n;++j)
{
Point x,y;
x=l[i].p;
y=l[j].p;
if(x!=y)
{
if(check(x,y))
{
pd=1;
break;
}
}
x=l[i].p;
y=l[j].q;
if(x!=y)
{
if(check(x,y))
{
pd=1;
break;
}
}
x=l[i].q;
y=l[j].p;
if(x!=y)
{
if(check(x,y))
{
pd=1;
break;
}
}
x=l[i].q;
y=l[j].q;
if(x!=y)
{
if(check(x,y))
{
pd=1;
break;
}
}
}
}
if(pd==1)
printf("Yes!\n");
else
printf("No!\n");
}
return 0;
}