解题思路
题意:在一个平面上,给你n个线段,问是否存在一条直线,使得将这些线段投影到上面之后,所有投影线段至少有一个共同点。
PS:投影就是线段的两个端点向直线作垂线所包含的区域。
我们可以把题意转换一下,投影有一个公共的交点就相当于有一条与那条直线的垂直的直线必过所有线段。
然后我们就去找是否有一条直线与所有线段相交就好了。考虑这样的直线不止一条,且它通过平移,旋转,肯定会与线段的端点相交。两点确定一条直线,我们枚举两个线段的端点作为直线,判断是否存在一条这样的直线与所有线段都相交就好了。
PS:判断直线与线段相交只用做一次跨立实验,即线段跨立直线。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define ll long long
#define db double
using namespace std;
bool ok,flag;
int T,n,nn;
struct node {
db x,y,xx,yy;
} v[110],a[110];
db work(node a,node b,node c) {
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
bool hh(node a,node b,node c) {
if(c.x>=min(a.x,b.x)&&c.x<=max(a.x,b.x)&&c.y>=min(a.y,b.y)&&c.y<=max(a.y,b.y))
return 1;
return 0;
}
bool check(db ax,db ay,db bx,db by,db cx,db cy,db dx,db dy) {
node a,b,c,d;
a.x=ax,a.y=ay,b.x=bx,b.y=by,c.x=cx,c.y=cy,d.x=dx,d.y=dy;
if(work(c,b,d)*work(c,d,a)>0)
return 1;
if(work(a,b,d)==0&&hh(a,b,d))return 1;
if(work(a,b,c)==0&&hh(a,b,c))return 1;
if(work(c,d,a)==0&&hh(c,d,a))return 1;
if(work(c,d,b)==0&&hh(c,d,b))return 1;
return 0;
}
int main() {
scanf("%d",&T);
while(T--) {
flag=0;
scanf("%d",&n);
nn=n;
for(int i=1; i<=n; i++) {
scanf("%lf%lf%lf%lf",&a[i].x,&a[i].y,&a[i+n].x,&a[i+n].y);
v[i].x=a[i].x,v[i].y=a[i].y,v[i].xx=a[i+n].x,v[i].yy=a[i+n].y;
}
n=n*2;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
ok=1;
if(i==j) continue;
for (int k=1; k<=nn; k++)
if(!check(v[k].x,v[k].y,v[k].xx,v[k].yy,a[i].x,a[i].y,a[j].x,a[j].y)) {
ok=0;
break;
}
if(ok==1) {
flag=1;
break;
}
}
if(flag==1)break;
}
if(flag)
printf("Yes!\n");
else printf("No!\n");
}
}