[计算几何]求半平面交的面积

版权声明:作为一个蒟蒻,转载时请通知我这个蒟蒻 https://blog.csdn.net/zyszlb2003/article/details/89892135

题面描述

传送门

思路

先看一下SCY的视频吧,讲得真的很清(luo)楚(suo)。

忽略掉一些奇奇怪怪的语言

首先我们先来了解一下SCY开头说的 a x + b y + c = 0 ax+by+c=0

也就是下面这条直线?

在这里插入图片描述
我稍微纳闷了一会,这不是 y = k x + b y=kx+b 吗?

稍微转换一下?

a x + b y + c = 0 ax+by+c=0

b y = a x c by=-ax-c

y = a b x c b y=-\frac{a}{b}x-\frac{c}{b}

这不就出来了吗。

这个东西我们以后会用到的。


a t a n atan 函数就是已知边长,逆向求近似度数的一个玄学函数。

就如下图:

在这里插入图片描述

t a n θ = 3.49 7.99 a t a n ( 3.49 , 7.99 ) = θ tan\theta=\frac{3.49}{-7.99},atan(3.49,-7.99)=\theta

特别地, θ \theta 是弧度制

void get_angle(){angle=atan2(p2.y-p1.y,p2.x-p1.x);}

这句话实际上的意思就为将这条有向的线段当作向量(其实就是向量),将起点挪到原点处,求出它的 a t a n atan 值。

之后我们用 a t a n atan 来排序

bool cmp(segment sg1,segment sg2)
{
		if(sg1.angle<sg2.angle)return true;
}

这个排序实际上是将线段(直线)的方向有序化了,大概是这样一个形状(此处是已经经过挪移的形状):

在这里插入图片描述

此处按字典序小的排在前面为排序后的结果。


那么有一些特殊的情况,

比如

两条带方向的直线平行(图中仅给出线段,因为可以从线段中推出直线):

注意这里的平行实际意义上为 s g 1. a n g l e = = a g 2. a n g l e sg1.angle==ag2.angle
在这里插入图片描述

此时我们应该选择 p 1 p_1 还是 p 2 p_2 呢,

选择 p 1 p_1 ,因为 p 2 p 1 = p 1 p_2箭头半平面\cup p_1箭头左边的半平面=p_1箭头左边的半平面

我们要求是半平面的交集。

fabs(sg1.angle-sg2.angle)<eps判断是否平行

pd(sg1.p1,sg2)判断 s g 1 sg1 是否在 s g 2 sg2 左面

那么现在 p 1 p_1 的位置先于 p 2 p_2 的位置。


完整的cmp

bool cmp(segment sg1,segment sg2)
{
	if(sg1.angle<sg2.angle)return true;
	if(fabs(sg1.angle-sg2.angle)<eps&&pd(sg1.p1,sg2))return true;
	return false;
}

接下来就 p d pd 函数:

bool pd(point p,segment sg)//是否在此直线左侧 
{
	if(mul(p,sg.p2,sg.p1)<=eps)return true;//叉乘之积为负数即为左侧
	return false;
}

个人认为 j d jd 是通篇最难的一个函数了。

先讲讲被SCY蓝掉的那个 j d jd

因为:

它要推柿子

我们要用到上面的结论直线 a x + b y + c = 0 ax+by+c=0

首先我们要解一个解析式方程组求半平面交点

{ a 1 x + b 1 y + c 1 = 0 a 2 x + b 2 y + c 2 = 0 \begin{cases}a_1x+b_1y+c_1=0\\a_2x+b_2y+c_2=0\end{cases}

我们要求的是 x , y x,y

首先我们看看怎么求一组 ( a 1 , b 1 , c 1 ) (a_1,b_1,c_1) 吧。

首先 p 1 , p 2 p_1,p_2 点在 a 1 x + b 1 y + c 1 = 0 a_1x+b_1y+c_1=0 这条直线上了。

{ a 1 p 1 . x + b 1 p 1 . y + c 1 = 0 a 1 p 2 . x + b 1 p 2 . y + c 1 = 0 \begin{cases}a_1p_1.x+b_1p_1.y+c_1=0\cdots ①\\a_1p_2.x+b_1p_2.y+c_1=0\cdots②\end{cases}

①式-②式,得 a 1 ( p 1 . x p 2 . x ) + b 1 ( p 1 . y p 2 . y ) = 0 a_1(p_1.x-p_2.x)+b_1(p_1.y-p_2.y)=0

发现这是一个不定方程。

求特解

解得: a 1 = p 1 . y p 2 . y , b 1 = p 2 . x p 1 . x a_1=p_1.y-p_2.y,b_1=p_2.x-p_1.x

正好满足。

a 1 , b 1 a_1,b_1 代入①式就有:

p 1 . x p 1 . y p 1 . x p 2 . y + p 2 . x p 1 . y p 1 . x p 1 . y + c 1 = 0 p_1.x*p_1.y-p_1.x*p_2.y+p_2.x*p_1.y-p_1.x*p_1.y+c_1=0

p 2 . x p 1 . y p 1. x p 2 . y + c 1 = 0 p_2.x*p_1.y-p1.x*p_2.y+c_1=0

c 1 = p 1 . x p 2 . y p 2 . x p 1 . y c_1=p_1.x*p_2.y-p_2.x*p_1.y

a 2 , b 2 , c 2 a_2,b_2,c_2 同理可得。

a 2 = p 3 . y p 4 . y , b 2 = p 4 . x p 3 . x , c 2 = p 3 . x p 4 . y p 4 . x p 3 . y a_2=p_3.y-p_4.y,b_2=p_4.x-p_3.x,c_2=p_3.x*p_4.y-p_4.x*p_3.y

之后就是解方程了

{ a 1 x + b 1 y + c 1 = 0 a 2 x + b 2 y + c 2 = 0 \begin{cases}a_1x+b_1y+c_1=0\\a_2x+b_2y+c_2=0\end{cases}

{ a 1 x + b 1 y = c 1 a 2 x + b 2 y = c 2 \begin{cases}a_1x+b_1y=-c_1\\a_2x+b_2y=-c_2\end{cases}

{ a 1 x = c 1 b 1 y a 2 x = c 2 b 2 y \begin{cases}a_1x=-c_1-b_1y\\a_2x=-c_2-b_2y\end{cases}

{ x = c 1 + b 1 y a 1 x = c 2 + b 2 y a 2 \begin{cases}x=-\frac{c_1+b_1*y}{a_1}\\x=-\frac{c_2+b_2*y}{a_2}\end{cases}

c 1 + b 1 y a 1 = c 2 + b 2 y a 2 \frac{c_1+b_1*y}{a_1}=\frac{c_2+b_2*y}{a_2}

( c 1 + b 1 y ) a 2 = ( c 2 + b 2 y ) a 1 (c_1+b_1*y)*a_2=(c_2+b_2*y)*a_1

c 1 a 2 + a 2 b 1 y = c 2 a 1 + a 1 b 2 y c_1*a_2+a_2*b_1*y=c_2*a_1+a_1*b_2*y

( a 2 b 1 a 1 b 2 ) y = c 2 a 1 c 1 a 2 (a_2*b_1-a_1*b_2)y=c_2*a_1-c_1*a_2

y = c 2 a 1 c 1 a 2 a 2 b 1 a 1 b 2 y=\frac{c_2*a_1-c_1*a_2}{a_2*b_1-a_1*b_2}

同理

x = c 1 b 2 c 2 b 1 a 2 b 1 a 1 b 2 x=\frac{c_1*b_2-c_2*b_1}{a_2*b_1-a_1*b_2}

终于写完一个了。


SCY标程中的那个其实是更加简化的一个东西:

x = c 1 b 2 c 2 b 1 a 2 b 1 a 1 b 2 = ( p 1 . x p 2 . y p 2 . x p 1 . y ) ( p 4 . x p 3 . x ) ( p 3 . x p 4 . y p 4 . x p 3 . y ) ( p 2 . x p 1 . x ) ( p 3 . y p 4 . y ) ( p 2 . x p 1 . x ) ( p 1 . y p 2 . y ) ( p 4 . x p 3 . x ) x=\frac{c_1*b_2-c_2*b_1}{a_2*b_1-a_1*b_2}=\frac{(p_1.x*p_2.y-p_2.x*p_1.y)*(p_4.x-p_3.x)-(p_3.x*p_4.y-p_4.x*p_3.y)*(p_2.x-p_1.x)}{(p_3.y-p_4.y)*(p_2.x-p_1.x)-(p_1.y-p_2.y)*(p_4.x-p_3.x)}

= ( p 1 . x p 2 . y p 2 . x p 1 . y ) ( p 4 . x p 3 . x ) ( p 3 . x p 4 . y p 4 . x p 3 . y ) ( p 2 . x p 1 . x ) p 2 . x p 3 . y p 1 . x p 3 . y p 2 . x p 4 . y + p 1 . x p 4 . y p 4 . x p 1 . y + p 3 . x p 1 . y p 4 . x p 2 . y + p 3 . x p 2 . y \small=\frac{(p_1.x*p_2.y-p_2.x*p_1.y)*(p_4.x-p_3.x)-(p_3.x*p_4.y-p_4.x*p_3.y)*(p_2.x-p_1.x)}{p_2.x*p_3.y-p_1.x*p_3.y-p_2.x*p_4.y+p_1.x*p_4.y-p_4.x*p_1.y+p_3.x*p_1.y-p_4.x*p_2.y+p_3.x*p_2.y}

= ( p 1 . x p 2 . y p 2 . x p 1 . y ) ( p 4 . x p 3 . x ) ( p 3 . x p 4 . y p 4 . x p 3 . y ) ( p 2 . x p 1 . x ) ( ( p 1 . x p 3 . x ) ( p 4 . y p 3 . y ) ( p 4 . x p 3 . x ) ( p 1 . y p 3 . y ) ) ( ( p 2 . x p 3 . x ) ( p 4 . y p 3 . y ) ) ( p 4 . x p 3 . x ) ( p 2 . y p 3 . y ) \small=\frac{(p_1.x*p_2.y-p_2.x*p_1.y)*(p_4.x-p_3.x)-(p_3.x*p_4.y-p_4.x*p_3.y)*(p_2.x-p_1.x)}{((p_1.x-p_3.x)*(p_4.y-p_3.y)-(p_4.x-p_3.x)*(p_1.y-p_3.y))-((p_2.x-p_3.x)*(p_4.y-p_3.y))-(p_4.x-p_3.x)(p_2.y-p_3.y)}

= m u l ( p 1 , p 4 , p 3 ) p 2 . x m u l ( p 2 , p 4 , p 3 ) p 1 . x m u l ( p 1 , p 4 , p 3 ) m u l ( p 2 , p 4 , p 3 ) =\frac{mul(p_1,p_4,p_3)*p_2.x-mul(p_2,p_4,p_3)*p_1.x}{mul(p_1,p_4,p_3)-mul(p_2,p_4,p_3)}

同理, y = m u l ( p 1 , p 4 , p 3 ) p 2 . y m u l ( p 2 , p 4 , p 3 ) p 1 . y m u l ( p 1 , p 4 , p 3 ) m u l ( p 2 , p 4 , p 3 ) y=\frac{mul(p_1,p_4,p_3)*p_2.y-mul(p_2,p_4,p_3)*p_1.y}{mul(p_1,p_4,p_3)-mul(p_2,p_4,p_3)}

很具体有木有?


接下来就是比较主要的部分了。

对于新加进来的一个半平面,

我们会出现以下的情况:

  1. !pd(jd(sglist[tail],sglist[tail-1]),seg[i]))也就是 s e g t a i l 1 , s e g t a i l + 1 seg_{tail-1},seg_{tail+1} 的交点在新半平面交(直线方向左边)外,如图:
    在这里插入图片描述
    2.同样的,也有:

在这里插入图片描述
所以我们要开一个类似单调队列的队列,来维护这个半平面交。

特别地,在所有线段都被遍历过之后,我们还需要:
while(head<tail&&!pd(jd(sglist[tail],sglist[tail-1]),sglist[head]))--tail;

这是因为:
在这里插入图片描述

接下来就是求交点了。

pl=0;for(int i=head+1;i<=tail;i++)plist[++pl]=jd(sglist[i-1],sglist[i]);
plist[++pl]=jd(sglist[head],sglist[tail]);

最后喜闻乐见求面积。

对了这道题还有边界。

SCY讲得还行。

就不赘叙了。

AC code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define eps 1e-8
using namespace std;
const int N=2e4+10;
const double Maxn=1e5;
const double Minn=-1e5;
struct point{double x,y;}plist[N];int pl;
struct segment
{
	point p1,p2;
	double angle;
	segment(){}
	segment(double x1,double y1,double x2,double y2)
		{p1.x=x1,p1.y=y1;p2.x=x2,p2.y=y2;angle=atan2(p2.y-p1.y,p2.x-p1.x);}
}sglist[N],seg[N];int n,head,tail;
double mul(point p1,point p2,point p0)
{
	double x1=p1.x-p0.x,x2=p2.x-p0.x;
	double y1=p1.y-p0.y,y2=p2.y-p0.y;
	return x1*y2-x2*y1;
}
point jd(segment sg1,segment sg2)
{
	double t1=mul(sg1.p1,sg2.p2,sg2.p1);
	double t2=mul(sg1.p2,sg2.p2,sg2.p1);
	point p1=sg1.p1,p2=sg1.p2,p;
	p.x=(t1*p2.x-t2*p1.x)/(t1-t2);
	p.y=(t1*p2.y-t2*p1.y)/(t1-t2);
	return p;
}
bool pd(point p,segment sg){return mul(p,sg.p2,sg.p1)<=eps;}
bool cmp(segment sg1,segment sg2)
{
	if(sg1.angle<sg2.angle)return true;
	if(fabs(sg1.angle-sg2.angle)<eps&&pd(sg1.p1,sg2))return true;
	return false;
}
void SI()
{
	sort(seg+1,seg+n+1,cmp);
	int tp=1;for(int i=2;i<=n;i++)if(seg[i].angle-seg[tp].angle>eps)seg[++tp]=seg[i];
	n=tp;head=1;tail=2;
	sglist[1]=seg[1];sglist[2]=seg[2];
	for(int i=3;i<=n;i++)
	{
		while(head<tail&&!pd(jd(sglist[tail],sglist[tail-1]),seg[i]))--tail;
		while(head<tail&&!pd(jd(sglist[head],sglist[head+1]),seg[i]))++head;
		sglist[++tail]=seg[i];
	}
	while(head<tail&&!pd(jd(sglist[tail],sglist[tail-1]),sglist[head]))--tail;
	if(tail-head+1<=2){puts("0.0");return ;}
	pl=0;for(int i=head+1;i<=tail;i++)plist[++pl]=jd(sglist[i-1],sglist[i]);
	plist[++pl]=jd(sglist[head],sglist[tail]);
	double ans=0;for(int i=3;i<=pl;i++)ans+=mul(plist[i-1],plist[i],plist[1]);
	printf("%.1lf\n",fabs(ans)/2.0);
}
int main()
{
	scanf("%d",&n);
	seg[1]=segment(Maxn,Maxn,Minn,Maxn);
	seg[2]=segment(Minn,Maxn,Minn,Minn);
	seg[3]=segment(Minn,Minn,Maxn,Minn);
	seg[4]=segment(Maxn,Minn,Maxn,Maxn);
	for(int i=1;i<=n;i++)
	{
		double x1,y1,x2,y2;scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
		seg[i+4]=segment(x1,y1,x2,y2);
	}
	n+=4;
	SI();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zyszlb2003/article/details/89892135
今日推荐