【POJ 3304】Segments【计算几何】

在这里插入图片描述


解题思路

题意:在一个平面上,给你n个线段,问是否存在一条直线,使得将这些线段投影到上面之后,所有投影线段至少有一个共同点。

PS:投影就是线段的两个端点向直线作垂线所包含的区域。

我们可以把题意转换一下,投影有一个公共的交点就相当于有一条与那条直线的垂直的直线必过所有线段。
然后我们就去找是否有一条直线与所有线段相交就好了。考虑这样的直线不止一条,且它通过平移,旋转,肯定会与线段的端点相交。两点确定一条直线,我们枚举两个线段的端点作为直线,判断是否存在一条这样的直线与所有线段都相交就好了。

PS:判断直线与线段相交只用做一次跨立实验,即线段跨立直线。


代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define ll long long
#define db double
using namespace std;

bool ok,flag;
int T,n,nn;

struct node {
	db x,y,xx,yy;
} v[110],a[110];

db work(node a,node b,node c) {
	return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}

bool hh(node a,node b,node c) {
	if(c.x>=min(a.x,b.x)&&c.x<=max(a.x,b.x)&&c.y>=min(a.y,b.y)&&c.y<=max(a.y,b.y))
		return 1;
	return 0;
}

bool check(db ax,db ay,db bx,db by,db cx,db cy,db dx,db dy) {
	node a,b,c,d;
	a.x=ax,a.y=ay,b.x=bx,b.y=by,c.x=cx,c.y=cy,d.x=dx,d.y=dy;
	if(work(c,b,d)*work(c,d,a)>0)
		return 1;
	if(work(a,b,d)==0&&hh(a,b,d))return 1;
	if(work(a,b,c)==0&&hh(a,b,c))return 1;
	if(work(c,d,a)==0&&hh(c,d,a))return 1;
	if(work(c,d,b)==0&&hh(c,d,b))return 1;
	return 0;
}

int main() {
	scanf("%d",&T);
	while(T--) {
		flag=0;
		scanf("%d",&n);
		nn=n;
		for(int i=1; i<=n; i++) {
			scanf("%lf%lf%lf%lf",&a[i].x,&a[i].y,&a[i+n].x,&a[i+n].y);
			v[i].x=a[i].x,v[i].y=a[i].y,v[i].xx=a[i+n].x,v[i].yy=a[i+n].y;
		}
		n=n*2;
		for(int i=1; i<=n; i++) {
			for(int j=1; j<=n; j++) {
				ok=1;
				if(i==j) continue;
				for (int k=1; k<=nn; k++) 
					if(!check(v[k].x,v[k].y,v[k].xx,v[k].yy,a[i].x,a[i].y,a[j].x,a[j].y)) {
						ok=0;
						break;
					}
				if(ok==1) {
					flag=1;
					break;
				}
			}
			if(flag==1)break;
		}
		if(flag)
			printf("Yes!\n");
		else printf("No!\n");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值