半平面交

部分转自:http://www.cnblogs.com/yzcstc/archive/2013/04/11/3015521.html

半平面交是求多边体的核,也即是类似求看多边形里面存不存在可以看到边上所有点的点的这种题


所谓半平面,通俗一点就是就是一些形如 a*x+b*y+c <=0 (或 >=0)的区域,实际上就是平面的一半,成为半平面,

而半平面交,就是一些这样的半平面解的交集,反映上直角坐标系上就是半平面的共同区域

而求半平面的的方法一般有3种:


1)联机算法,这是一种朴素的做法,即对新加入的边判断原来的交点是否在合法的区域内,不合法就该点,并加入新点。。

最终的点就是合法的点。复杂度o(n^2)

2)分治法。即利用二分的思想 将 N条边划分成2个n/2,利用递归求解。算法复杂度o(nlogn)

3)增量排序法。此方法是zzy大神2006国家集训队论文中提出的。。下面具体操作

Step1:对于所有的直线进行极角排序,如果极角相同,保留需要的一个(这几个极角相同的全部为公共解的那一个)

step2:使用双端队列,加入初始的两个半平面

step3:依次加入新的半平面,并判断原来队尾的2个半平面交点符不符合当前的半平面,不符合删除队尾的最后一个元素

判断原来队头的2个半平面交点符不符合当前的半平面,不符合删除队头的第一一个元素

将半平面加到队尾,重复step3操作。。

step4:判断当前队头跟队尾有木有矛盾(有矛盾就是队头的前两个元素交点不再队尾班平面区域内),删除队头的第一个元素

队尾同样操作。。

step5,判断有木有解。。(一般判断剩下的直线,少于3条就无解)


以下为联机算法:

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
#define Vector Point
const double EPS=1e-6;
int Sign(double x){ 
	if(fabs(x)<EPS) return 0;
	return x<0?-1:1;
}
struct Point{
	double x,y;
	Point(double x,double y):x(x),y(y){}
	Vector operator - (const Point& p){
		return Vector(x-p.x,y-p.y);
	}
	double operator * (const Vector& v){
		return x*v.x+y*v.y;
	}
}; 
double cross(const Point& a,const Point& b){
	return a.x*b.y-a.y*b.x;
}
Vector operator * (double k,Vector p){  //c=f*|a|;
	return Vector(k*p.x,k*p.y);
}
Vector operator + (Point a,Vector b){
	return Vector(a.x+b.x,a.y+b.y);
}
bool operator == (Point a,Point b){
	return a.x==b.x&&a.y==b.y;
}
struct Seg{
	Point a,b;
	Seg(const Point& a,const Point& b):a(a),b(b){}
}; 

typedef vector<Point> Polygon;


//两条线段相交的各种情况
//结果保存在pair<int,Point> 中 
//返回值 result.first:
//0 规范相交,
//1 端点重合,但不平行,不共线
//2 一个端点在另一个内部 s1端点在 s2内部 (不平行,不共线)
//3 一个端点在另一个内部 s2端点在 s1内部 (不平行,不共线)
//4 无交点,不平行,不共线,两直线交点是result.second
//5 平行
//6 共线且有公共点
//7 共线且无公共点
//8 s1,s2无交点,但是s2所在直线和s1有交点,即交点在s1上
//9 s1,s2无交点,但是s1所在直线和s2有交点,即交点在s2上
bool FLessEq(double a,double b){ //b不小于a 
	return Sign(b-a)>=0;
} 
double length(Vector p){  //求|p| 
	return sqrt(p*p);
}
double dist(Point p,Seg s){
	return fabs(cross(p-s.a,s.b-s.a))/length(s.b-s.a); //面积除以高
}
bool PointInSeg(Point p,Seg L){  //p点在线段l内 
	double tmp = cross(L.a-p,L.a-L.b);
	if(Sign(tmp)) return false;
	if(FLessEq(min(L.a.x,L.b.x),p.x) &&
	   FLessEq(p.x,max(L.a.x,L.b.x)) &&
	   FLessEq(min(L.a.y,L.b.y),p.y) &&
	   FLessEq(p.y,max(L.a.y,L.b.y)) )
	   return true;
	return false;
} 
pair<int,Point> CrossPoint(Seg s1,Seg s2){
	Point p1=s1.a;
	Point p2=s1.b;
	Point p3=s2.a;
	Point p4=s2.b;
	
	double a1 = cross(p3-p1,p4-p1); 
	double a2 = cross(p4-p2,p3-p2);
	if(Sign(cross(p2-p1,p3-p1))*Sign(cross(p2-p1,p4-p1)) < 0
	   && Sign(cross(p4-p3,p1-p3))*Sign(cross(p4-p3,p2-p3)) < 0)
	   return make_pair(0,p1+(a1/(a1+a2))*(p2-p1)); //规范相交
	if(Sign(cross(p2-p1,p3-p4))){  //不平行不共线
		if(p1==p3||p1==p4) return make_pair(1,p1);
		if(p2==p3||p2==p4) return make_pair(1,p2);
		if(PointInSeg(p1,s2)) return make_pair(2,p1);
		if(PointInSeg(p2,s2)) return make_pair(2,p2);
		if(PointInSeg(p3,s1)) return make_pair(3,p3);
		if(PointInSeg(p4,s1)) return make_pair(3,p4);
		
		Point crossPoint = p1+(a1/(a1+a2))*(p2-p1); //交点 
		if(PointInSeg(crossPoint,s1)) return make_pair(8,crossPoint);
		if(PointInSeg(crossPoint,s2)) return make_pair(9,crossPoint);
		
		//直线和线段也无交点 不平行 不共线 两直线交点是second 
		return make_pair(4,crossPoint);  	
	} 
	if(Sign(dist(p1,s2))) return make_pair(5,Point(0,0));  //平行
	
	//共线  且有公共点 
	if(PointInSeg(p1,s2))   return make_pair(6,p1);
	if(PointInSeg(p2,s2))   return make_pair(6,p2);
	if(PointInSeg(p3,s1))   return make_pair(6,p3);
	if(PointInSeg(p4,s1))   return make_pair(6,p4);
	return make_pair(7,Point(0,0));//共线 且无公共点
}

int CutPloygon(Point a,Point b,const Polygon& src,Polygon& result)
{
	//直线a->b,切割src,返回其左边的多边形,放到result中 
	int n=src.size();  //src中的点并不需要排序 
	result.clear();
	for(int i=0;i<n;i++)
	{
		Point c=src[i];    //取出相邻的两个点 
		Point d=src[(i+1)%n];
		if(Sign(cross(b-a,c-a))>=0) //c点在a->b左侧或上面 
			result.push_back(c);
		pair<int,Point> r=CrossPoint(Seg(c,d),Seg(a,b));
		if(r.first==0||r.first==8||r.first==3) 
			result.push_back(r.second);	//相交的话把交点放入
	}
 } 



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值