POJ 3304 Segments【叉积】

题意:有n条线段,问有没有一条直线使得所有线段在这条直线上的投影至少有一个共同点。

思路:逆向思维,很明显这个问题可以转化为是否有一条直线穿过所有线段,若有,问题要求的直线与该直线垂直,并且公共点为垂足。

因此只需要枚举每两个端点形成的直线,判断是否和所有线段相交。证明,若存在一条与所有线段相交的直线L,则可以将L平移直到再移动就不满足"与所有线段相交"这个条件时,此时L经过某个线段的一个端点,再将L旋转直到再旋转就不满足“与所有线段相交"这个条件时,肯定与另一个端点相交。

判断直线与线段相交可以用

1,叉积的性质,若线段与直线相交,则线段两个端点在直线的两边,设t1为一端与直线的叉积,t2为另一端与直线的叉积,线段与直线相交时,t1 X t2<=0

2,设线段所在的直线为tmp,求L与tmp的交点p,若线段两端点到p的距离之和等于线段的长度,则线段与直线相交

坑点:

1,n=1时需要特判。

2,当选择的两个点重合时,会导致所有点与该直线的叉积一直小于0从而认为各个线段和该直线相交。

#include<stdio.h>
#include<string.h>
#include<math.h>
struct point{
    double x,y;
    point(){}
    point(double x_,double y_){
        x=x_,y=y_;
    }
    point operator -(const point &b)const{
        return point(x-b.x,y-b.y);
    }
    double operator *(const point &b)const{//点积 
        return x*b.x+y*b.y;
    }
    double operator ^(const point &b)const{//叉积 
        return x*b.y-y*b.x;
    }
};
double cal(point p0,point p1,point p2){//小于0表示在p1处左折,大于0右折,等于0同线 
    return (p1-p0)^(p2-p0);
}
const int N=111;
const double esp=1e-8;
point s[N],e[N];
int n;
int pd(point a,point b){
    if(fabs(a.x-b.x)<esp&&fabs(a.y-b.y)<esp)
        return 0;
    for(int i=1;i<=n;i++){
        if(cal(s[i],a,b)*cal(e[i],a,b)>esp)
            return 0;
    }
    return 1;
}
int main(){
    int t,i,j;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&s[i].x,&s[i].y,&e[i].x,&e[i].y);
        }
        int f=0;
        if(n<2)    f=1;
        for(i=1;i<=n&&!f;i++){
            for(j=i+1;j<=n&&!f;j++){
                if(pd(s[i],s[j]))    f=1;
                if(pd(s[i],e[j]))    f=1;
                if(pd(e[i],s[j]))    f=1;
                if(pd(e[i],e[j]))    f=1;
            }
        }
        if(f)    puts("Yes!");
        else    puts("No!"); 
    }
    return 0;
}

http://blog.sina.com.cn/s/blog_6635898a0100n2lv.html

http://www.cnblogs.com/staginner/archive/2012/02/07/2340835.html

  

原文地址:https://www.cnblogs.com/L-King/p/5727086.html