<模板><计算几何>半平面求交学习小记

主要是依靠这篇博文学习的 http://blog.csdn.net/accry/article/details/6070621

我的半平面交代码模板也参考自这里,个人进行了简单优化。


刚发现个很水的变换时针方法。如果你需要逆时针,而题目给的顺时针,那么读入的时候改成

		for(int i=n-1; i>=0; i--)
			scanf("%lf%lf",&p[i].x,&p[i].y);

二分double型改变大小的时候low=mid比low=mid+eps要好一些(个人感觉)


半平面求交 及 多边形求核 模板

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

const double EPS=1e-11;
const int NUM=1510;

int DB (double x)
{
    if (x>EPS)
        return 1;
    if (x<-EPS)
        return -1;
    return 0;
}

struct Point
{
    double x,y;
 
    Point(){}
    Point(double _x,double _y)
    {
        x=_x;
        y=_y;
    }
 
    void get ()
    {
		scanf("%lf%lf",&x,&y);
	}

    void Out ()
    {
        printf("(%.5lf %.5lf)",x,y);
    }
 
    Point operator+(Point a)
    {
        return Point(x+a.x,y+a.y);
    }
    Point operator-(Point a)
    {
        return Point(x-a.x,y-a.y);
    }
 
    double operator*(Point a)     //叉积
    {
        return x*a.y-y*a.x;
    }

	double operator^(Point a)     //点积
    {
        return x*a.y+y*a.x;
    }

    Point operator*(double t)
    {
        return Point(x*t,y*t);
    }
 
    Point operator/(double t)
    {
        return Point(x/t,y/t);
    }
 
    bool operator==(Point a)
    {
        return DB(x-a.x)==0&&DB(y-a.y)==0;
    }
 
    bool operator!=(Point a)
    {
        return DB(x-a.x)||DB(y-a.y);
    }
}points[NUM],p[NUM],q[NUM];

int n;
double r;
int cCnt,curCnt;

void getline (Point x,Point y,double &a,double &b,double &c)
{
	a = y.y - x.y;
	b = x.x - y.x;
	c = y.x * x.y - x.x * y.y;
}

Point intersect (Point x,Point y,double a,double b,double c)  //相交
{
    double u = fabs(a * x.x + b * x.y + c);
    double v = fabs(a * y.x + b * y.y + c);
    return Point( (x.x * v + y.x * u) / (u + v) , (x.y * v + y.y * u) / (u + v) );
}

/*半平面相交(直线切割多边形)(点标号从1开始)*/
void cut (double a,double b ,double c)
{
	int i;
	curCnt = 0;
	for (i=1;i<=cCnt;i++)
	{
        if (DB(a*p[i].x + b*p[i].y + c) >= 0)
			q[++curCnt] = p[i];
        else
		{
            if (DB(a*p[i-1].x + b*p[i-1].y + c) > 0)
                q[++curCnt] = intersect(p[i],p[i-1],a,b,c);
            if (DB(a*p[i+1].x + b*p[i+1].y + c) > 0)
                q[++curCnt] = intersect(p[i],p[i+1],a,b,c);
        }
    }
    for (i=1;i<=curCnt;i++)
		p[i] = q[i];
    p[curCnt+1] = q[1];
	p[0] = p[curCnt];
    cCnt = curCnt;
}

void GuiZhengHua ()
{
	//规整化方向,逆时针变顺时针,顺时针变逆时针
	for (int i=1;i<(n+1)/2;i++)
		swap(points[i], points[n-i]);
}

double getArea (Point p[],int n)
{
	double ans=0;
	for (int i=0;i<n;i++)
		ans+=p[i]*p[i+1];
	return ans/2;
}

void initial ()
{
    for (int i=1;i<=n;i++)
		p[i] = points[i];  
	p[n+1] = p[1];
	p[0] = p[n];
	cCnt = n;
	
	double s=getArea(p,n);     //自动规整化为顺时针,如果方向确定,可不执行
	if (DB(s)>=0)      //面积为正,说明为逆时针
		GuiZhengHua ();
}

bool Deal ()
{
	int i;
	//注意:默认点是顺时针,如果题目不是顺时针,规整化方向  
	initial();
	for (i=1;i<=n;i++)
	{
		double a,b,c;
		getline (points[i],points[i+1],a,b,c);
		cut(a,b,c);
	}       //p[0]~p[cCnt-1]及p[1]~p[cCnt]均存放多边形的核的cCnt-1个顶点

    /* 
    如果要向内推进r,用该部分代替上个函数 
    for (i=1;i<=n;i++)
	{ 
        Point ta, tb, tt; 
        tt.x = points[i+1].y - points[i].y; 
        tt.y = points[i].x - points[i+1].x; 
        double k = r / sqrt(tt.x * tt.x + tt.y * tt.y); 
        tt.x = tt.x * k; 
        tt.y = tt.y * k; 
        ta.x = points[i].x + tt.x; 
        ta.y = points[i].y + tt.y; 
        tb.x = points[i+1].x + tt.x; 
        tb.y = points[i+1].y + tt.y; 
        double a,b,c; 
        getline(ta,tb,a,b,c); 
        cut(a,b,c); 
    } 
    */
	// 计算面积
	printf("%.2lf\n",fabs(getArea (p,cCnt)));    //顺时针处理后面积为负,但写成-1.0*会WA……

	if (cCnt == 0)
		return false;
	return true;
} 

int main ()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		for (int i=1;i<=n;i++)
			points[i].get();
		points[0]=points[n];
		points[n+1] = points[1];
		Deal ();
	}
	return 0;
}

nlogn算法,Poj2451

个人感觉比较难理解,据说该算法特殊情况有bug……

代码参考自://http://hi.baidu.com/godwitness/item/8107cb1f6a21d7f987ad4e84

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

const double STD=1e-10,big=100000.0;
const int NUM = 20010;

struct Point
{
	double x,y;

	void get ()
	{
		scanf("%lf%lf",&x,&y);
	}
}node[NUM];

struct polygon   //存放最后半平面交中相邻边的交点,就是一个多边形的所有点
{
	int n;           //多边形顶点数
	Point p[NUM];    //从p[0]开始保存
}pg;

struct Line      //半平面,这里是线段
{
	Point a,b;
}data[NUM];        //半平面集合

double at2[NUM];   //每条线段对应坐标系的角度
int ord[NUM];
int dq[NUM+1];     //双端栈
int n,lnum;

int DB (double x)  
{  
    if (x>STD)  
        return 1;  
    if (x<-STD)  
        return -1;  
    return 0;  
}
 
//叉积>0代表在左边,<0代表在右边,=0代表共线
//e是否在o->s的左边onleft(DB(cross))>=0
double cross (Point o, Point s, Point e) 
{
	return (s.x-o.x)*(e.y-o.y)-(e.x-o.x)*(s.y-o.y);
}

//直线求交点
Point isIntersected (Point s1, Point e1, Point s2, Point e2)
{
	double dot1,dot2;
	Point pp;
	dot1 = cross(s2,e1,s1); dot2 = cross(e1,e2,s1); 
	pp.x = (s2.x * dot2 + e2.x * dot1) / (dot2 + dot1); 
	pp.y = (s2.y * dot2 + e2.y * dot1) / (dot2 + dot1); 
	return pp;
}
 
//象限排序
bool cmp (int u,int v)
{
	if (DB(at2[u]-at2[v]) == 0)
		return DB(cross(data[v].a,data[v].b,data[u].b))>=0;
	return at2[u]<at2[v];
} 

//判断半平面的交点在当前半平面外
bool Judgein (int x,int y,int z)
{
	Point pnt = isIntersected(data[x].a, data[x].b, data[y].a, data[y].b); //求交点
	return DB(cross(data[z].a,data[z].b,pnt)) < 0; //判断交点位置,如果在右面,返回true,如果要排除三点共线,改成<=
}

//半平面交
void HalfPlaneIntersection (polygon &pg)
{ //预处理
	int n=lnum,tmpn,i; 

	/* 对于atan2(y,x)
	结果为正表示从 X 轴逆时针旋转的角度,结果为负表示从 X 轴顺时针旋转的角度.
	atan2(a, b) 与 atan(a/b)稍有不同,atan2(a,b)的取值范围介于 -pi 到 pi 之间(不包括 -pi),
	而atan(a/b)的取值范围介于-pi/2到pi/2之间*/
	for (i=0;i<n;i++) 
	{ //atan2(y,x)求出每条线段对应坐标系的角度
		at2[i] = atan2( data[i].b.y - data[i].a.y, data[i].b.x - data[i].a.x);
		ord[i] = i;
	}
	sort (ord,ord+n,cmp);
	for (i=1,tmpn=1;i<n;i++) //处理重线的情况
		if( DB(at2[ord[i-1]] - at2[ord[i]]) != 0 )
			ord[tmpn++] = ord[i];
	n = tmpn; 
	//圈地
	int bot = 1,top = bot + 1; //双端栈,bot为栈底,top为栈顶
	dq[bot] = ord[0]; dq[top] = ord[1]; //先压两根线进栈
	for (i=2;i<n;i++)
    { 
		//bot < top 表示要保证栈里至少有2条线段,如果剩下1条,就不继续退栈
		//Judgein,判断如果栈中两条线的交点如果在当前插入先的右边,就退栈
		while ( bot < top && Judgein(dq[top-1] , dq[top] , ord[i]) )
			top--;
		//对栈顶要同样的操作
		while ( bot < top && Judgein(dq[bot+1] , dq[bot] , ord[i]) )
			bot++; 
		dq[++top] = ord[i]; 
	} 
	//最后还要处理一下栈里面存在的栈顶的线在栈底交点末尾位置,或者栈顶在栈尾两条线的右边
	while ( bot < top && Judgein(dq[top-1] , dq[top] , dq[bot]) )
		top--; 
	while ( bot < top && Judgein(dq[bot+1] , dq[bot] , dq[top]) )
		bot++; 
	//最后一条线是重合的
	dq[--bot] = dq[top]; 
	//求多边形
	pg.n = 0; 
	for (i=bot+1;i<=top;i++) //求相邻两条线的交点
		pg.p[pg.n++] = isIntersected(data[dq[i-1]].a, data[dq[i-1]].b, data[dq[i]].a,data[dq[i]].b);
}

void add (double a,double b,double c,double d)  //添加线段
{
	data[lnum].a.x = a; data[lnum].a.y = b; 
	data[lnum].b.x = c; data[lnum].b.y = d;
	lnum++;
}

void init ()           //计算时要求多边形的核一定位于半平面左侧
{ 
	int i;
	scanf("%d",&n);
/*	double a,b,c,d;
	for (i=0;i<n;i++)
	{
		//输入代表一条向量(x = (c - a),y = (d - b)),核在其左侧;
		scanf ("%lf%lf%lf%lf",&a,&b,&c,&d);
		add (a,b,c,d);
	}*/
	for (i=0;i<n;i++)       //以多边形顶点输入,要求逆时针
		node[i].get();
	data[n]=data[i];
	for (i=0;i<n;i++)
		add(node[i].x,node[i].y,node[i+1].x,node[i+1].y);
	//下面是构造一个大矩形边界
	add(0,0,big,0);       //down
	add(big,0,big,big);   //right
	add(big,big,0,big);   //up
	add(0,big,0,0);       //left
}

double Deal ()
{
	init ();
	HalfPlaneIntersection(pg); //求半平面交
	//最后多边形的各个点保存在pg里面


	//计算半平面交的面积
	double area = 0;
	n = pg.n;
	
	for (int i=0;i<n;i++) 
		area += pg.p[i].x * pg.p[(i+1)%n].y - pg.p[(i+1)%n].x * pg.p[i].y;    //x1 * y2 - x2 * y1用叉积求多边形面积 
	area=fabs(area)/2.0;           //所有面积应该是三角形面积之和,而叉积求出来的是四边形的面积和,所以要除2

	return area;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值