您的位置:首页 > 其它

POJ 3304 Segment 直线与线段相交

2013-07-30 19:31 295 查看
Segments

Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 8017Accepted: 2411
Description

Given n segments in the two dimensional space, write a program, which determines if there exists a line such that after projecting these segments on it, all projected segments have at least one point in common.

Input

Input begins with a number T showing the number of test cases and then, T test cases follow. Each test case begins with a line containing a positive integer n ≤ 100 showing the number of segments. After that,n lines containing
four real numbers x1 y1 x2 y2 follow, in which (x1, y1) and (x2, y2) are the coordinates of the
two endpoints for one of the segments.

Output

For each test case, your program must output "Yes!", if a line with desired property exists and must output "No!" otherwise. You must assume that two floating point numbers a and b are equal if |a - b| < 10-8.

Sample Input
3
2
1.0 2.0 3.0 4.0
4.0 5.0 6.0 7.0
3
0.0 0.0 0.0 1.0
0.0 1.0 0.0 2.0
1.0 1.0 2.0 1.0
3
0.0 0.0 0.0 1.0
0.0 2.0 0.0 3.0
1.0 1.0 2.0 1.0

Sample Output
Yes!
Yes!
No!

题目大意:

给出n条线段,问是否存在一条直线,使得所给的线段均投影到该直线上,并且这些投影在这条直线上至少有一个公共点。

解题思路:

当我们试着画两条线段,把他们最接近的两个端点用线连接起来,然后再从连线的中间作一条垂线,我们会发现,把这两条线段投影到这条垂线上,绝对会产生公共点。于是我们可以把问题转化为,求是否存在一条直线穿过所有的线段,如果有的话,他的垂线就是题目要找的直线。我们可以想象,如果存在这条垂线,那么我们可以在保证与所有线段相交的情况下,轻轻的旋转移动,那么总会出现这种情况:某线段的一个端点和另外一个线段的一个端点正好在这条线上。于是我们可以枚举不同线段上的端点,让他们两个端点产生一条直线,从而判断这条直线是否能穿过所有线段,如果可以那么我们就输出Yes!,否则我们输出No!

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define eps 1e-10
int n;
struct point
{
        double x;
        double y;
};
struct line
{
        point a,b;
}p[105];
double xmult(point p1,point p2,point p0) //求叉积
{
        return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
double dis(point p1,point p2)  //求距离
{
        return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
bool judge(int i,int j,point p1,point p2)  //判断枚举的直线和所有的线段是否相交
{
     int k;
     for(k=0;k<n;k++)
     {
          if(k==i||k==j)
               continue;
          if(xmult(p1,p[k].a,p2)*xmult(p1,p[k].b,p2)>eps||dis(p1,p2)<eps)  //用叉乘相乘>0可知两条线不相交,距离相差1e-10以内算同一点
               return false;  //有一个不相交返回false
     }
     return true; //全都相交返回true
}
int main()
{
        int cas,i,j;
        bool flag;
        scanf("%d",&cas);
        while(cas--)
        {
                scanf("%d",&n);
                for(i=0;i<n;i++)
                        scanf("%lf%lf%lf%lf",&p[i].a.x,&p[i].a.y,&p[i].b.x,&p[i].b.y);
                flag=true;
                for(i=0;i<n-1;i++)           //把第i条线段和第j条线段分别拿出一个端点组成一条直线
                {
                        for(j=i+1;j<n;j++)
                        {
                             flag=false;
                             if(judge(i,j,p[i].a,p[j].a)||judge(i,j,p[i].a,p[j].b)||judge(i,j,p[i].b,p[j].a)||judge(i,j,p[i].b,p[j].b))
                             flag=true;
                             if(flag)
                                  break;
                        }
                        if(flag)
                             break;
                }
                if(flag)
                        printf("Yes!\n");
                else
                        printf("No!\n");
        }
        system("pause");
        return 0;
}


技巧总结:

这一题的方法非常巧妙,计算几何中仅仅牵涉到线段的问题应该是很容易做出来的,无非就是用到叉积、点积、跨立实验等方法,所以当不知如何下手的时候,应该也要往这几个方面去想,去转化问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: