[几何][离散化]Segments

本文介绍了一种通过检查多个线段是否存在共同交点的方法。利用斜率和端点坐标进行判断,实现了一个高效的算法,该算法能判断在一组线段中是否存在一条直线贯穿所有线段。

题目描述
在平面中给定n个线段,编写一个程序,确定是否存在一条直线,使得在描述这些线段的位置之后,所有线段至少有一个重合点。

输入

输入以t开头,显示测试样例的数量,然后,t个测试样例在接下来的行数。每个测试样例以一行包含一个正整数n≤100显示线段的数量。在那之后,N行包含四个实数x1,y1,x2,y2跟随,以(x1,y1),(x2,y2)是一个线段的两个端点坐标。

输出

对于每个测试样例,您的程序必须输出“Yes!”如果有符合题目要求的行存在,必须输出“No!”否则。你必须假设A和B两浮点数相等,如果| A,B |<10-8。

分析
首先我们发现如果所需要的直线是存在的话,那么我们可以不断枚举它的斜率来找到这条直线,但是因为斜率是个实数,枚举它肯定不大可能。
因为是个无法枚举的很大的数,所以我想到了离散
这时候再推论一下,当这条直线存在时,它斜率偏转的直线肯定在两个线段的任意端点上
那么就比较简单了,无限的点离散到数组里,就只用枚举极端情况了
枚举两个线段,然后判断过任意两个端点的直线是否过其他所有线段,若是则就找到了

#include <iostream>
#include <cstdio>
#include <cmath>
#define sm 0.00000001
using namespace std;
int t,n;
struct F
{
    double x,y;
}s[101][2];
int i,j;
bool b;
double cp(double x1,double y1,double x2,double y2,double x0,double y0)
{
    double u=(x1-x0)*(y2-y0)-(x2-x0)*(y1-y0);
    return u;
}
bool dist(double x1,double y1,double x2,double y2)
{
    return abs(x1-x2)<sm&&abs(y1-y2)<sm;
}
bool across(double x1,double y1,double x2,double y2)
{
    if (dist(x1,y1,x2,y2)) return false;
    for (int i=1;i<=n;i++)
    if (cp(x1,y1,x2,y2,s[i][0].x,s[i][0].y)*cp(x1,y1,x2,y2,s[i][1].x,s[i][1].y)>sm) return false;
    return true;
}
int main()
{
    scanf("%d",&t);
    while (--t>=0)
    {
        scanf("%d",&n);
        for (i=1;i<=n;i++)
        scanf("%lf%lf%lf%lf",&s[i][0].x,&s[i][0].y,&s[i][1].x,&s[i][1].y);
        b=0;
        if (n<=2) b=1;
        else
        for (i=1;i<=n;i++)
        {
            for (j=i+1;j<=n;j++)
            {
                if (across(s[i][0].x,s[i][0].y,s[j][0].x,s[j][0].y))
                b=1;
                if (across(s[i][0].x,s[i][0].y,s[j][1].x,s[j][1].y))
                b=1;
                if (across(s[i][1].x,s[i][1].y,s[j][0].x,s[j][0].y))
                b=1;
                if (across(s[i][1].x,s[i][1].y,s[j][1].x,s[j][1].y))
                b=1;
                if (b)
                break;
            }
            if (b)
            break;
        }
        if (b) printf("Yes!\n");
        else printf("No!\n");
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值