POJ 3304-Segments(计算几何)

写在前面

第一次做这道题的时候,其实还有点蒙(学得太差了,连叉积都没有理解透就开始做题了)

然而在学习,抄袭借鉴了ldx巨佬的博客以后我才明白。

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 xyxy2 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.

Solution

ldx:反正n<=100随便做啊

注意到n<=100,那么我们肯定不需要一个O(N)或者O(NlogN)的算法,所以可以尽情的想如何暴力解决这道题,原题说的是要找到一条直线使得所有线段在直线上的投影至少有一个公共点,显然直接找会很麻烦(反正我不会),那么变一下形。怎么变?如果说线段们的投影有公共点的话,那么可以说是过线段的两个端点做垂线以后得到的新线段有公共点,这些线段如果有公共点,那么可以过这个(或者这些)公共点做条垂线(相当于把投影还原),那么这条(或者这些)直线必然穿过所有线段,反之亦然。

所以说我们就可以去找这样一条直线,那么怎么找呢?如果说这条直线穿过了所有的线段的中间(即不经过端点),那么我们可以把它往下移一点点,使得直线经过一个端点,然而只过一个端点我们还是不会做,那么我们可以旋转一下,让这条线过两个端点,那么问题就十分简单了,判断这条直线是否穿过所有线段即可。

综上所述,我们可以得到一个算法:枚举两个端点,并判断它们构成的直线是否过所有线段即可,而在判断的时候用叉积来计算线段的两个端点在直线同侧还是异侧,具体见代码实现。

Talk is cheap, show you the code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct Point{
	double x,y;
	Point(double x=0,double y=0):x(x),y(y){}
}p[10005];
const double eps=1e-10;
int n;
typedef Point Vector;
Vector operator + (Vector a,Vector b){
	return Vector(a.x+b.x,a.y+b.y);
}
Vector operator - (Vector a,Vector b){
	return Vector(a.x-b.x,a.y-b.y);
}
int dcmp(double x){
	if(fabs(x)<eps) return 0;
	else return x<0?-1:1;
}
double Dot(Vector a,Vector b){
	return a.x*b.x+a.y*b.y;
}
double Cross(Vector a,Vector b){
	return a.x*b.y-a.y*b.x;
}
bool ok(Point a,Point b,Point c,Point d){
	if((Cross(a-c,b-c)*Cross(a-d,b-d))<=0.00000000)return true;
	return false;
}
bool judge(Point a,Point b){
	for(int i=1;i<n;i+=2) if(ok(a,b,p[i],p[i+1])==0) return false;
	return true;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		n<<=1;
		for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
		bool flag=false;
		for(int i=1;i<n;i++)
			for(int j=i+1;j<=n;j++){
				if(dcmp(p[i].x-p[j].x)==0&&dcmp(p[i].y-p[j].y)==0) continue;
				if(judge(p[i],p[j])) flag=true;
			}
		if(flag) puts("Yes!");
		else puts("No!");
		//memset(p,0,sizeof(p));
	}
	return 0;
} 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值