判断直线与线段相交 POJ 3304 Segments

题意:在二维平面中,给定一些线段,然后判断在某直线上的投影是否有公共点。

转化,既然是投影,那么就是求是否存在一条直线L和所有的线段都相交。

证明:

下面给出具体的分析:先考虑一个特殊的情况,即n=1的时候,如下图,线段AB在直线L上的投影为线段A'B',则过任意介于A'B'之间的点C'做直线L的垂线必交线段AB与一点C;反之,过线段AB之间任意一点C做直线L的垂线,垂足必定落在A'B'之间。

                               

 

     不难将此结论推广到n条线段的情况,假设存在一满足题意的直线L,则设点A为各个线段在L上投影的公共点,那么过A做一条直线L的垂线L',则L'必定与n条线段都相交;反之,过所有线段做一个直线L1使其与n条线段均相交,做直线L1的垂线L2,容易发现垂足即为所有n条线段投影的公共点。                                                   

                                ---------摘自http://blog.csdn.net/Once_HNU/article/details/6327906

  判断直线和线段相交,这个和判断线段和线段相交差不多,从直线上任取两个点,和线段的端点比较,如果两个在同一侧,那么就不相交,反之,就相交。关键问题就是怎么找这个直线, 假设存在一条直线跟所有线段都相交,我们可以让这个直线旋转,条件是旋转之后仍然相交,所有一定有个临界条件,而且这个临界条件一定发生在线段的端点处, 所以,可以通过枚举所有 线段端点的方式来求这个直线。

还有需要注意的是: 

1.  n = 1, n = 2时要特判,因为2条线段一定可以找第三条直线与他们相交

2. 如果枚举线段端点的时候,两个点的距离小于10^-8,那么就跳过此直线。由于计算机在浮点类型的计算中1e-8会当成三点共线来算,所以不能用它来当成要求的那条直线。

代码如下:

 

/*************************************************************************
    > File Name: poj_3304.cpp
    > Author: 
    > Mail: 
    > Created Time: 2015年04月01日 星期三 19时01分05秒
 ************************************************************************/

#include<iostream>
#include <cstdio>
#include <math.h>
#define EPS 1e-8
using namespace std;
struct point{
    double x, y;
};
const int N = 230;
point p[N];
int T, n;
//直线的叉积,来判断点在直线ab的哪一侧
double get_direction(point a, point b, point c)
{
    point t1, t2;
    t1.x = c.x - a.x; t1.y = c.y - a.y;
    t2.x = b.x - a.x; t2.y = b.y - a.y;
    return (t1.x * t2.y - t1.y * t2.x);
}
//算两点之间的距离
double get_distance(int i, int j)
{
    return sqrt((p[j].x - p[i].x) * (p[j].x - p[i].x) + (p[j].y - p[i].y) * (p[j].y - p[i].y));
}
int main()
{
    scanf("%d", &T);
    while (T--)
    {
        double d1, d2;
        bool flag = false;
        scanf("%d", &n);
        for (int i = 0; i < 2 * n; i++)
            scanf("%lf %lf", &p[i].x, &p[i].y);
        if (n == 1 || n == 2)//特判
        {
            puts("Yes!");
            continue;
        }
        for (int i = 0; i < 2 * n; i++)
        {
            for (int j = i + 1; j < 2 * n; j++)//这两层for是枚举所有的线段端点所在直线
            {
                if (get_distance(i, j) < EPS)//精度
                    continue;
                bool tmp_flag = true;
                for (int k = 0; k < 2 * n; k++)
                {
                    d1 = get_direction(p[i], p[j], p[k]);
                    d2 = get_direction(p[i], p[j], p[++k]);
                    if (d1 * d2 > 0)
                    {
                        tmp_flag = false;
                        break;
                    }
                }
                if (tmp_flag)//如果找到直接跳出循环
                {
                    flag = true;
                    break;
                }
            }
            if (flag)//优化,找到之后直接跳出循环
                break;
        }
        if (flag)
            puts("Yes!");
        else
            puts("No!");
    }

    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Howe-Young/p/4385198.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值