题解
- 所有线段的投影都交于一点,相当于有一条直线的垂线经过所有的线段。
- 直线经过所有的点,我们可以旋转直线,使它恰好经过线段的两个端点,所有O(n^2)枚举端点。
- 判断ab直线和线段cd是否相交,只要判ab×ac和ab×ad的符号是否小于等于0。比判断线段和线段相交少一个条件。
- 最后注意重点的判断,因为叉积为0可能是直线和线段重合。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
double const eps = 1e-12;
int const N = 100 + 10;
int n;
typedef struct Point{
double x,y;
Point(){};
Point(double x,double y):x(x),y(y){};
Point operator - (const Point& e)const{
return Point(x - e.x,y - e.y);
}
}Vector;
struct Line{
Point a,b;
}line[N];
int dcmp(double x){ //判断符号
if(fabs(x) < eps) return 0;
else return x < 0 ? -1 : 1;
}
double cross(Vector a,Vector b){ //向量a×b
return a.x * b.y - a.y * b.x;
}
bool Judge(Vector a,Vector b){ //直线ab
if(dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0) return false; //重点
for(int i=0;i<n;i++)
if(dcmp(cross(a - b,a - line[i].a)) * dcmp(cross(a - b,a - line[i].b)) > 0) return false;
return true;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lf%lf%lf%lf",&line[i].a.x,&line[i].a.y,&line[i].b.x,&line[i].b.y);
bool flag = false;
for(int i=0;i<n && !flag;i++)
for(int j=0;j<n && !flag;j++){ //端点两两组合有四种情况
if(Judge(line[i].a,line[j].a) || Judge(line[i].a,line[j].b)
||Judge(line[i].b,line[j].a) || Judge(line[i].b,line[j].b))
flag = true;
}
if(flag) printf("Yes!\n");
else printf("No!\n");
}
return 0;
}