POJ 3304 Segments(思维+判断直线和线段相交关系)

题目链接


题目大意

给出 n n n个线段,判断在二维坐标系内是否存在一条直线使得所有线段在该直线上的投影满足至少相交于一个点。

解题思路

倒过来想,先画出一条直线,并假设最坏的情况如果所有投影只有一个点,随便构造几条线段,就会发现线段都有一个端点在投影点经过的直线上。再扩大成一段重复的投影线段,并构造一些满足条件的线段,能发现存在两条线段端点使构成的直线经过所有线段。而且给出的线段数量也不是很多,大胆猜测直接枚举任意两个端点构成的直线是否经过所有线段即可

判断线段和直线相交的方法,就是一次快速排斥实验,我在入门计算几何时总结有直线和线段各种问题的博客,本题最大的坑就是,可能有线段共享端点,我们知道枚举时必须特判两个是否为重复点,但是下面的方法显然是不行的,因为这样只是保证不枚举自身,但后面还可能出现重复的点,除非离散化后使用这种方法

for(int i=0;i<num && !flag;i++)
	for(int j=0;j<i && !flag;j++)

代码:

#include <iostream>
#include <math.h>
using namespace std;
#define Point Vector
#define Line Seg
const double eps=1e-8;

int dcmp(double d){
    if(fabs(d)<eps) return 0;
    if(d>0) return 1;
    return -1;
}

struct Point{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
    Point operator + (Point p){ return Point(x+p.x,y+p.y); }
    Point operator - (Point p){ return Point(x-p.x,y-p.y); }
    Point operator * (double d){ return Point(x*d,y*d); }
    double operator * (Point p){ return x*p.x+y*p.y; }
    Point operator / (double d){ return Point(x/d,y/d); }
    double operator ^ (Point p){ return x*p.y-y*p.x; }
    bool operator == (const Point &p) const { return dcmp(x-p.x)==0 && dcmp(y-p.y)==0; }
};

double dis(Point a,Point b){
    return sqrt((b-a)*(b-a));
}

struct Line{
    Point p,q;
    Vector v;
    Line(){}
    Line(Point a,Point b){
        p=a,q=b,v=b-a;
    }
};

bool isOnLine(Point p,Line l){
    return dcmp((l.p-p)^(l.q-p))==0?true:false;
}

bool isSegLineInter(Seg s,Line l){
    double c1=l.v^(s.p-l.p),c2=l.v^(s.q-l.p);
    return dcmp(c1)*dcmp(c2)<=0;
}

Point t[1005];
Seg s[200];

int main()
{
    int kase,n;
    double a,b,c,d;
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>kase;
    while(kase--){
        cin>>n;
        int num=0,cnt=0;
        Point aa,bb;
        while(n--){
            cin>>a>>b>>c>>d;
            aa=Point(a,b),bb=Point(c,d);
            t[num++]=aa;
            t[num++]=bb;
            s[cnt++]=Line(aa,bb);
        }
        int flag=0;
        for(int i=0;i<num && !flag;i++){
            for(int j=0;j<num && !flag;j++){
                if(dcmp(dis(t[i],t[j]))==0) continue;  //坑!!!
                flag=1;
                Line l=Line(t[i],t[j]);
                for(int k=0;k<cnt;k++){
                    if(!isSegLineInter(s[k],l)){
                        flag=0;
                        break;
                    }
                }
            }
        }
        if(flag) cout<<"Yes!\n";
        else cout<<"No!\n";
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值