POJ 3304 Segments(直线与线段相交)

题意

题目链接

给定n条线段,确定是否存在一条直线,使得这n条线段在这条直线上的投影具有公共点。

n<=100

Sol

非常妙的一个题。

我们考虑如果所有线段的投影有重合部分,那么我们可以在重合部分上做一条垂线经过所有点

同时我们平移一下这个直线,一定可以与某个点重合。

然后考虑旋转一下,一定可以交于某个最近的点(最近的定义是旋转最小的角度与之相交)

那么我们就搞出了一个\(n^3\)的做法:暴力枚举两个点构成的直线,判断是否与所有点相交

判断直线与线段相交可以用叉积

如果线段上的两点与直线的端点连线的叉积均同号的话,说明线段在直线的两侧。

否则说明相交

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN = 1001;
const double eps = 1e-10;
int N;
struct Point {
    double x, y;
    Point operator - (const Point &rhs) const {
        return {x - rhs.x, y - rhs.y};
    }
    double operator ^ (const Point &rhs) const {
        return x * rhs.y - y * rhs.x;
    }
    bool operator == (const Point &rhs) const {
        return (x - rhs.x) < eps && (y -rhs.y) < eps;
    }
};
struct L {
    Point a, b;
};
Point line[MAXN][2];
int dcmp(double x) {
    if(fabs(x) < eps) return 0;
    return x > 0 ? 1 : -1;
}
bool judge(L l1, L l2) {
    Point tmp = l1.a - l1.b;
    bool flag = dcmp(dcmp(tmp ^ (l2.b - l1.b)) * dcmp(tmp ^ (l2.a - l1.b))) != 1;
    return flag;
}
bool check(Point a, Point b) {
    if(a == b) return 0;
    for(int i = 1; i <= N; i++)
        if(!judge({a, b}, {line[i][0], line[i][1]})) return 0;
    return 1;
}
void solve() {
    scanf("%d", &N);
    for(int i = 1; i <= N; i++) 
        scanf("%lf %lf %lf %lf", &line[i][0].x, &line[i][0].y, &line[i][1].x, &line[i][1].y);
    for(int i = 1; i <= N; i++)
        for(int j = 0; j < 2; j++)
            for(int k = 1; k <= N; k++)
                for(int l = 0; l < 2; l++)
                    if(check(line[i][j], line[k][l])) {puts("Yes!"); return ;}
    puts("No!");
}
int main() {
    int T; scanf("%d", &T);
    for(; T; T--, solve());
}

转载于:https://www.cnblogs.com/zwfymqz/p/10375511.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值