洛谷P4196 半平面交

题意:

给出平面上 n n n个多边形,求他们的面积交。

方法:

若以每条边为直线,多边形逆时针的点序为直线的方向,则多边形的面积交可以看作是每条边所在的直线的左侧平面的面积交,我们按逆时针来找出这个平面交多边形。

因为极角排序完之后向量可以看作是逆时针旋转的,所以我们先极角排序,同时,如果极角相同的话,有用的边应该是更靠近多边形核的那一个,在这里我们认为是更靠左的,相对位置可以用一条直线的点和另一条直线做叉积判断。

那么我们考虑按极角顺序加入边,上两次加入的边所形成的交点应该就是多边形的一个顶点,那么我们当前加入的直线如果在上两个的交点的左侧,那么这个交点显然是没有现在形成的好的,因为多边形的交一定出现在每一条直线的左侧,那么我们就需要像凸包一样舍去他。同时因为我们维护的多边形还需要考虑加入直线和最开始的两条直线形成的交点位置,所以我们可能需要舍去前面加入的,所以将栈升级为双端队列。

并且,每次加入直线时,我们都需要先考虑队尾,再考虑队首。考虑下面这一张图

在这里插入图片描述

此时加入 a a a向量,上一次形成的点在我们的右侧,不符合情况,并且此时只存在两个边, M M M点形成的更大责任应该是 v v v直线的锅,因为我们是逆时针加入的,后加入的权重更大些,所以要先考虑队尾。

在加入完所有的直线进队列后,因为每次加入都有下一次边来考虑我们现在形成的点是否合法,而最后一个加入完就退出循环了,因此我们需要额外判断一次最后的点是否合法,最后一条边的下一条边就是第一条边了,此时不是加入新边,只是单纯判断上次的点是否合法,所以只需要考虑最后的交点和第一次的直线即可,不用即考虑队首又考虑队尾。

另外,在多边形基础上考虑这个面积交是一定有解的,但或许会只给出一些直线求围成的面积?此时可能是无穷大无解,有一种判断无解的方法,加入四条平行坐标轴的直线,把给出的点都框起来,如果我们在队列里发现这四条直线了,那就是无解了。

#include<bits/stdc++.h>
using namespace std;

const double eps=1e-8,pi=acos(-1);

int cmp(double a,double b)
{
	if(fabs(a-b)<eps) return 0;
	else if(a-b>eps) return 1;
	return -1;
}

struct vec
{
	double x,y;
	bool operator==(const vec& a) const {return (x-a.x)<eps&&(y-a.y)<eps;}
	vec operator+(const vec& a) const {return (vec){x+a.x,y+a.y};}
	vec operator-(const vec& a) const {return (vec){x-a.x,y-a.y};}
	vec operator*(const double& k) const {return (vec){x*k,y*k};}
	vec operator/(const double& k) const {return (vec){x/k,y/k};}
	double operator^(const vec& a) const {return x*a.y-y*a.x;}//获得叉积的模
	double operator*(const vec& a) const {return x*a.x+y*a.y;}
	friend istream& operator>>(istream& o,const vec& a)
	{
		scanf("%lf%lf",&a.x,&a.y);
		return o;
	}
	friend ostream& operator<<(ostream& o,const vec& a)
	{
		printf("%.10lf %.10lf\n",a.x,a.y);
		return o;
	}
};

struct line
{
	vec base,a,b;//两条向量和方向向量
};

bool rui(const vec& a,const vec& b){return a*b>eps;}//判定是否是锐角
bool zhi(const vec& a,const vec& b){return fabs(a*b)<eps;}//是否直角
bool dun(const vec& a,const vec& b){return a*b<-eps;}//是否钝角
bool operator!=(const vec& a,const vec& b){return !(a==b);}
double S(const vec& a,const vec& b){return fabs(a^b)/2;}//面积,叉积的模/2
vec operator*(const double& k,const vec &a){return (vec){k*a.x,k*a.y};}
double getlen(const vec& a){return sqrt(a.x*a.x+a.y*a.y);}//模长
double getangle(const vec& a,const vec& b){return acos(a*b/(getlen(a)*getlen(b)));}//获得夹角
vec getvertical(const vec& a){return (vec){a.y,-a.x};}//外积
vec getsingle(const vec& a){return a/getlen(a);}//单位向量
vec rotate(const vec& a,double k){return (vec){a.x*cos(k)-a.y*sin(k),a.x*sin(k)+a.y*cos(k)};}//旋转k弧度
line trans(const vec& a,const vec& b){return (line){getsingle(b-a),a,b};}//两点确定一条直线
line trans(double a,double b,double x,double y){return (line){getsingle((vec){x-a,y-b}),(vec){a,b},(vec){x,y}};}//两点确定一条直线

vec cross(const line& l1,const line& l2)
{
    double k=((l2.a-l1.a)^l2.base)/(l1.base^l2.base);
    return l1.a+k*l1.base;
}

int n;
vector<line>ll;
line q[10005];
int sta=1,en;

bool in(const line& l,const vec& p)
{
    return cmp((p-l.a)^l.base,0)<0;
}

bool out(const line& l,const vec& p)
{
    return cmp((p-l.a)^l.base,0)>0;
}

int main()
{
    // freopen("in.txt","r",stdin);
	cin>>n;
    vector<vec>v;
    for(int i=1;i<=n;i++)
    {
        v.clear();
        int t; scanf("%d",&t);
        while(t--)
        {
            vec temp; cin>>temp;
            v.push_back(temp);
        }
        v.push_back(v[0]);
        for(int j=1;j<v.size();j++) ll.push_back(trans(v[j-1],v[j]));
    }
    sort(ll.begin(),ll.end(),[&](const line& l1,const line& l2){
        double t1=atan2(l1.base.y,l1.base.x),t2=atan2(l2.base.y,l2.base.x);
        if(t1<0) t1+=2*pi; if(t2<0) t2+=2*pi;
	    if(fabs(t1-t2)>eps) return t1<t2;
	    return in(l2,l1.a);
    });
    for(auto l:ll)
    {
        if(en-sta+1>=1&&cmp(l.base^q[en].base,0)==0) continue;
        while(en-sta+1>=2&&out(l,cross(q[en],q[en-1]))) en--;
        while(en-sta+1>=2&&out(l,cross(q[sta],q[sta+1]))) sta++;
        q[++en]=l;
    }
    while(en-sta+1>=2&&out(q[sta],cross(q[en],q[en-1]))) en--;
    if(en-sta+1<3)
    {
        printf("0.000\n");
        return 0;
    }
    q[en+1]=q[sta];
    vector<vec>ans;
    for(int i=sta;i<=en;i++) ans.push_back(cross(q[i],q[i+1]));
    ans.push_back(ans[0]);
    double ret=0;
    for(int i=1;i<ans.size();i++) ret+=ans[i-1]^ans[i];
    printf("%.3lf",ret/2);
  	return 0;
    double ret=0;
    for(int i=1;i<ans.size();i++) ret+=ans[i-1]^ans[i];
    printf("%.3lf",ret/2);
  	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值