杭电 1086 You can Solve a Geometry Problem too 判断线段是否相交.

判断两线段是否相交  有一个比较容易理解的快速排斥试验.
首先我们获得两条线段 我们首先需要把两个线段都自行扩张成为一个矩形 如下图:
根据观察就能知道 如果两个矩形不相交的话 两条线段是一定不会相交的.
所以通过快速排斥试验我们就能直接pass掉很多情况.
如果给出四个点(两条线段)我们可以通过以下代码实现判断快速排斥:

    if(!(min(a.x,b.x)<=max(c.x,d.x) && min(c.y,d.y)<=max(a.y,b.y)&&
        min(c.x,d.x)<=max(a.x,b.x) && min(a.y,b.y)<=max(c.y,d.y)))


以下是跨立试验的过程:
判断线段是否相交问题.要用到叉乘向量的知识  这里贴上向量叉乘的函数:
struct point

{

      double x,y;

};

void chacheng(point  a,point b,point c)
{
    return (c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y);
}

在线段判断是否相交的函数中用处:
判断向量bc在向量ab的顺时针方向还是逆时针方向.
下边图是我盗来的图~对应理解一下吧0.0
现在请读者在纸上画出两个图 一个是线段ab和cd相交的图 一个是不相交的图
然后对应如下代码体会其作用:
   
    u=(c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y);//c.b.a//u的正负表示bc在ab的顺时针方向还是逆时针方向.
    v=(d.x-a.x)*(b.y-a.y)-(b.x-a.x)*(d.y-a.y);//d.b.a//同理
u。v只考虑其大于0小于0还是为0(只考虑其实正数 负数还是0)
表示两条线段的所处几何方向
这时候你画的图就有用了~
如果ab和cd相交的情况下 向量bc是在ab的顺时针方向,并且向量bd是在ab的逆时针方向.是不是有点柳暗花明的感觉了~?
别着急 .我们继续看.如果确实有以上结论了是否就能判断ab和cd一定相交了呢?
这里上个图:
图片转载声明:http://blog.csdn.net/lishuhuakai/article/details/8263160
对应理解一下 只有一组满足是不够的~~~~~
所以这里还需要再填上另外一组  :
    w=(a.x-c.x)*(d.y-c.y)-(d.x-c.x)*(a.y-c.y);//a.d.c
    z=(b.x-c.x)*(d.y-c.y)-(d.x-c.x)*(b.y-c.y);//b.d.c


所以如果有了
u*v<=0.00000001 && w*z<=0.00000001
我们就能判断出两线段是一定相交的了~

下边给上AC完整代码:


#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
struct dian
{
    double x,y;
};

struct xian
{
    dian a;dian b;
};
//判断两条线段是否相交 首先一定要通过快速排斥试验之后 再通过跨立试验之后就能判断了这两线段相交.
bool judge(dian a,dian b,dian c,dian d)
{
    //快速排斥
    
    if(!(min(a.x,b.x)<=max(c.x,d.x) && min(c.y,d.y)<=max(a.y,b.y)&&
        min(c.x,d.x)<=max(a.x,b.x) && min(a.y,b.y)<=max(c.y,d.y)))
        //判断两条线段组成的矩形是否相交 如果不相交 那么这两条线是不会相交的.
        //特别要注意一个矩形含于另一个矩形之内的情况
    return false;
    double u,v,w,z;
    //定a定c.
    //c.d在ab两端 a.b在cd两端.就能判断相交.
    u=(c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y);//c.b.a//u的正负表示bc在ab的顺时针方向还是逆时针方向.
    v=(d.x-a.x)*(b.y-a.y)-(b.x-a.x)*(d.y-a.y);//d.b.a//同理
    w=(a.x-c.x)*(d.y-c.y)-(d.x-c.x)*(a.y-c.y);//a.d.c
    z=(b.x-c.x)*(d.y-c.y)-(d.x-c.x)*(b.y-c.y);//b.d.c
    return (u*v<=0.00000001 && w*z<=0.00000001);

}

int main()
{
int n;
    xian str[101];
    while(cin>>n && n!=0)
    {
        int count=0;
        for(int i=0;i<n;i++)
        {
            cin>>str[i].a.x>>str[i].a.y>>str[i].b.x>>str[i].b.y;
        }
        for(int i=0;i<n;i++)//遍历所有点什么的还是比较容易理解的~.
            for(int j=i+1;j<n;j++)
                if(judge(str[i].a,str[i].b,str[j].a,str[j].b)) count++;
        cout<<count<<endl;
    }
    return 0;
}



你可能感兴趣的:(杭电,线段相交,计算几何,杭电oj,1086)