POJ3304 Segments 【线段直线相交】

题意:

给出n条线段两个端点的坐标,问所有线段投影到一条直线上,如果这些所有投影至少相交于一点就输出Yes!,否则输出No!。

思路:

计算几何。这道题要思考到两点:

1:把问题转化为是否存在一条直线与每条线段都有交点。证明:若存在一条直线l和所有线段相交,作一条直线m和l垂直,则m就是题中要求的直线,所有线段投影的一个公共点即为垂足。

2:枚举两两线段的各一个端点,连一条直线,再判断剩下的线段是否都和这条直线有交点。证明:若有l和所有线段相交,则可保持l和所有线段相交,左右平移l到和某一线段交于端点停止(“移不动了”)。然后绕这个交点旋转。也是转到“转不动了”(和另一线段交于其一个端点)为止。这样就找到了一个新的l满足题意,而且经过其中两线段的端点。

判断线段与直线l是否相交的方法:

1:利用叉积的性质,判断线段的两个端点是否在直线的两边。

2:求线段所在的直线tmp,求tmp与l的交点p,由线段两端点到p的距离之和,与线段的距离比较,若相等则证明线段与直线相交。

代码:

#include<iostream>
#include<cmath>
using namespace std;
const int maxn = 105;
const double eps = 1e-8;
int n;

struct Point
{
    double x, y;
}s[maxn], e[maxn];

double mult(Point sp, Point ep, Point op)
{
    return (sp.x-op.x)*(ep.y-op.y) - (ep.x-op.x)*(sp.y-op.y);
}

bool findd(Point p1, Point p2)
{
    if(abs(p1.x-p2.x) < eps && abs(p1.y-p2.y) < eps)
        return false;
    for(int i = 0; i < n; i ++)
        if(mult(p1, p2, s[i])*mult(p1, p2, e[i]) > eps) return false;
    return true;
}

int main()
{
    int t, i, j;
    cin >> t;
    while(t --)
    {
        cin >> n;
        for(i = 0; i < n; i ++)
            cin >> s[i].x >> s[i].y >> e[i].x >> e[i].y;
        bool flag = false;
        if(n < 3) flag = true;
        for(i = 0; i < n && !flag; i ++)
            for(j = i + 1; j < n && !flag; j ++)    //  枚举线段的端点。
            {
                if(findd(s[i], s[j])) flag = true;
                else if(findd(s[i], e[j])) flag = true;
                else if(findd(e[i], s[j])) flag = true;
                else if(findd(e[i], e[j])) flag = true;
            }
           if(flag) cout << "Yes!" << endl;
           else cout << "No!" << endl;
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/darklights/p/7640426.html