POJ3304 Segments(计算几何,线段和直线的交点)

Segments
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 15325 Accepted: 4859

Description

Given n segments in the two dimensional space, write a program, which determines if there exists a line such that after projecting these segments on it, all projected segments have at least one point in common.

Input

Input begins with a number T showing the number of test cases and then, T test cases follow. Each test case begins with a line containing a positive integer n ≤ 100 showing the number of segments. After that, n lines containing four real numbers x1 y1 x2 y2 follow, in which (x1y1) and (x2y2) are the coordinates of the two endpoints for one of the segments.

Output

For each test case, your program must output "Yes!", if a line with desired property exists and must output "No!" otherwise. You must assume that two floating point numbers a and b are equal if |a - b| < 10-8.

Sample Input

3
2
1.0 2.0 3.0 4.0
4.0 5.0 6.0 7.0
3
0.0 0.0 0.0 1.0
0.0 1.0 0.0 2.0
1.0 1.0 2.0 1.0
3
0.0 0.0 0.0 1.0
0.0 2.0 0.0 3.0
1.0 1.0 2.0 1.0

Sample Output

Yes!
Yes!
No!


题意:给出一些线段,问是否存在直线,使得所有线段在该直线上的投影都有交点。

思路:如果能够找到一条直线经过所有的线段,那么这条直线的垂线即满足条件的直线。

如果存在直线经过所有的线段,那么一定存在一条直线经过所有线段且至少经过两个线段的端点。

枚举端点判断线段相交即可。



#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include <iostream>
#include <string>
#include <set>
#include <map>
using namespace std;
const int MAXN = 500+10;
const int INF=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
//计算几何误差修正
//输入为一个double类型的数,返回-1表示负数,1表示正数,0表示x为0
int cmp(double x){
    if(fabs(x)<eps)
        return 0;
    if(x>0) return 1;
    return -1;
}

//计算几何点类
inline double sqr(double x){
    return x*x;
}

struct point{
    double x,y;
    double xx;
    point(){}
    point(double a,double b):x(a),y(b){}
    void input(){
        scanf("%lf%lf",&x,&y);
    }
    //加法
    friend point operator + (const point &a,const point &b){
        return point(a.x+b.x,a.y+b.y);
    }
    //减法
    friend point operator - (const point &a,const point &b){
        return point(a.x-b.x,a.y-b.y);
    }
    //判断相等
    friend bool operator == (const point &a,const point &b){
        return cmp(a.x-b.x)==0&&cmp(a.y-b.y)==0;
    }
    //倍增
    friend point operator * (const point &a,const double &b){
        return point(a.x*b,a.y*b);
    }
    //倍增
    friend point operator * (const double &b,const point &a){
        return point(a.x*b,a.y*b);
    }
    //除法
    friend point operator / (const point &a,const double b){
        return point(a.x/b,a.y/b);
    }
    //模长
    double norm(){
        return sqrt(sqrt(x)+sqr(y));
    }
};

//叉积,a×b>0代表a在b的顺时针方向,<0代表a在b的逆时针方向,等于0代表a和b向量共线,但不确定方向是否相同
double det(const point &a,const point &b){
    return a.x*b.y-a.y*b.x;
}
//点积
double dot(const point &a,const point &b){
    return a.x*b.x+a.y*b.y;
}
//距离
double dist(const point &a,const point &b){
    return (a-b).norm();
}
//op向量绕原点逆时针旋转A(弧度)
point rotate_point(const point &p,double A){
    double tx=p.x,ty=p.y;
    return point(tx*cos(A)-ty*sin(A),tx*sin(A)+ty*cos(A));
}

//计算几何线段类
struct line{
    point a,b;
    line(){}
    line(point x,point y):a(x),b(y){}
    void input(){
        a.input();
        b.input();
    }
};

//用两个点a,b生成的一个线段或者直线
line point_make_line(point a,point b){
    return line(a,b);
}

//求点p到线段st的距离
double dis_point_segment(point p,point s,point t){
    if(cmp(dot(p-s,t-s))<0) return (p-s).norm();
    if(cmp(dot(p-s,s-t))<0) return (p-t).norm();
    return fabs(det(s-p,t-p)/dist(s,t));
}
//求点p到线段st的垂足,保存在cp中
void PointProjLine(point p,point s,point t,point &cp){
    double r=dot((t-s),(p-s))/dot(t-s,t-s);
    cp=s+(t-s)*r;
}

//判断p点是否在线段st上
bool PointOnSegment(point p,point s,point t){
    return cmp(det(p-s,t-s))==0&&cmp(dot(p-s,p-t))<=0;
}

//判断a和b是否平行
bool parallel(line a,line b){
    return !cmp(det(a.a-a.b,b.a-b.b));
}

//判断a和b是否共线
bool contribution(line a,line b){
    if(!parallel(a, b))
        return false;
    if(!cmp(det(a.b-a.a,b.a-a.b)))
        return true;
    return false;
}

//判断a和b是否相交,若相交则返回true且交点保存在res中
bool line_make_point(line a,line b,point &res){
    if(parallel(a, b))
        return false;
    double s1=det(a.a-b.a,b.b-b.a);
    double s2=det(a.b-b.a,b.b-b.a);
    res=(s1*a.b-s2*a.a)/(s1-s2);
    return true;
}
//判断线段是否相交
bool segment_make_point(line a,line b,point &res){
    if(!line_make_point(a,b,res))
        return false;
    if(PointOnSegment(res, a.a, a.b)&&PointOnSegment(res, b.a, b.b))
        return true;
    return false;
}
//判断线段和直线是否相交,a是直线,b是线段
bool line_across_segment(line a,line b){
    if(cmp(det(a.b-a.a,b.a-a.a)*det(a.b-a.a,b.b-a.a))==1){
        return false;
    }
    return true;
}

//将直线a沿法向量方向平移距离len得到的直线
line move_d(line a,const double &len){
    point d=a.b-a.a;
    d=d/d.norm();
    d=rotate_point(d, pi/2);
    return line(a.a+d*len,a.b+d*len);
}

line l[MAXN];
int n;
line ans;
bool check(){
    for(int i=0;i<n;i++){
        point temp;
        if(!line_across_segment(ans, l[i])&&!contribution(ans, l[i]))
            return false;
    }
    return true;
}
int main(){
    int t;
    //printf("%.16f\n",eps);
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            l[i].input();
        }
        if(n==1||n==2){
            puts("Yes!");
            continue;
        }
        int ok=0;
        for(int i=0;i<n-1&&!ok;i++){
            for(int j=i+1;j<n;j++){
                ans.a=l[i].a,ans.b=l[i].b;
                if(check()){
                    ok=1;
                    break;
                }
                ans.a=l[j].a,ans.b=l[j].b;
                if(check()){
                    ok=1;
                    break;
                }
                for(int p=0;p<2;p++){
                    for(int q=0;q<2;q++){
                        if(p)
                            ans.a=l[i].a;
                        else
                            ans.a=l[i].b;
                        if(q)
                            ans.b=l[j].a;
                        else
                            ans.b=l[j].b;
                        if(cmp(dist(ans.a, ans.b))==0){
                            continue;
                        }
                        if(check()){
                            ok=1;
                            break;
                        }
                    }
                    if(ok)
                        break;
                }
                if(ok)
                    break;
            }
        }
        if(ok){
            puts("Yes!");
        }else{
            puts("No!");
        }
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值