1. 程式人生 > >poj 3304 Segments(判斷直線與線段相交)

poj 3304 Segments(判斷直線與線段相交)

題目連結:poj 3304

題目大意:給出n條線段兩個端點的座標,問所有線段投影到一條直線上,如果這些所有投影至少相交於一點就輸出Yes!,否則輸出No!。

解題思路:

如果存在L的話,能說明什麼。。說明L的法線L’肯定能經過所有的線段

若存在一條直線L‘能經過所有線段,說明存在L’經過所有線段的兩個端點。

 

直線肯定經過兩個端點。

那麼我們只要列舉兩個端點P1P2,對每個直線P1P2 遍歷所有線段i:1~N,用叉積<=0來判斷線段的兩個端點L1L2是否在P1P2的兩側,如果找到一組P1P2就能輸出YES了

向量P和向量Q,假如 P*Q>0 ,P在Q的順時針,P*Q<0,則P在Q的逆時針,P*Q=0,則P與Q共線。

(L1P1^L1P2 )*(L2P1^L2P2 )<=0        ^代表外積,如下圖


注意:要判斷重複點,即P1P2有可能重點,那麼只要加判一下就好了。



#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

struct point
{
    double x,y;
    point(){}
    point(double _x,double _y){
        x=_x;y=_y;
    }
};

point operator + (point a,point b) {return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) {return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p) { return point(a.x*p,a.y*p);}
point operator / (point a,double p){ return point(a.x/p,a.y/p);}

struct line{
    point S,E;
    line(){}
    line(point _a,point _b){
        S=_a;E=_b;
    }
}segment[110];


bool operator < (const point &a,const point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x){
    if(fabs(x)<esp) return 0;
    else return x<0?-1:1;
}
bool operator ==(const point &a,const point &b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}

double Cross(point a,point b) { return a.x*b.y-a.y*b.x;}
double area2(point a,point b, point c) {return Cross(b-a,c-a);}
double Dot(point A,point B) { return A.x*B.x+A.y*B.y;}///2,點積,即是|a||b|*cos<a,b>,可用來判斷角度的範圍
double Length(point A) {return sqrt(Dot(A,A));} ///兩點之間的距離

int n;
bool check(line A)
{
    if(dcmp(Length(A.E-A.S))==0) return 0;

    for(int i=0;i<n;i++) ///判斷該直線A是否相交所有線段
    {
        if(dcmp(area2(segment[i].S,A.S,A.E))*dcmp(area2(segment[i].E,A.S,A.E))>0)
            return false;
    }
    return true;
}

int main()
{
    int ncase;
    scanf("%d",&ncase);

    double x1,y1,x2,y2;

    while(ncase--)
    {
        scanf("%d",&n);

        for(int i=0;i<n;i++){
        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
        segment[i]=line(point(x1,y1),point(x2,y2));
        }

        bool flag=false;

        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                ///列舉兩條線段i,j的某兩個端點作為直線
                if(check(line(segment[i].S,segment[j].S))||check(line(segment[i].S,segment[j].E))
                   ||check(line(segment[i].E,segment[j].S))||check(line(segment[i].E,segment[j].E))){
                    flag=true;
                    break;
                    }
            }
        }

        if(flag) puts("Yes!");
        else puts("No!");


    }
    return 0;
}