SGU209 Areas 【平面图】

题目大意:

给出n条直线,问直线围成的所有闭合区域的个数及面积,按面积大小升序输出。(n<=80)

解题思路:

①我们需要求出两两直线的交点;
②再对每条直线上的交点排序,来求出所有分割成的线段并连边(正反两条边);
③对于连向一个点的几条线段,对它们进行极角排序进行处理,这样就可以对于每条边记录走过去后下一条该走那条边了,我们脑补一下应该可以知道是它排序后相邻边的反向边。
④找循环,利用叉积求面积。

代码中用vector方便去重点,重边。

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

const int N=105,M=20005;
const double eps=1e-8;
struct point
{
    double x,y;
    point(){}
    point(double _x,double _y):x(_x),y(_y){}
    inline friend point operator + (const point &a,const point &b)
    {return point(a.x+b.x,a.y+b.y);}
    inline friend point operator - (const point &a,const point &b)
    {return point(a.x-b.x,a.y-b.y);}
    inline friend point operator * (const point &a,const double &b)
    {return point(a.x*b,a.y*b);}
    inline friend double operator * (const point &a,const point &b)
    {return a.x*b.x+a.y*b.y;}
    inline friend double operator ^ (const point &a,const point &b)
    {return a.x*b.y-a.y*b.x;}
    inline friend bool operator < (const point &a,const point &b)
    {return a.x-b.x<-eps||(abs(a.x-b.x)<eps&&a.y-b.y<-eps);}
    inline friend bool operator == (const point &a,const point &b)
    {return abs(a.x-b.x)<eps&&abs(a.y-b.y)<eps;}
    inline double len(){return sqrt(x*x+y*y);}
    inline point unit(){double d=len();return point(x/d,y/d);}
    inline double ang(){return atan2(y,x);}
};
struct line
{
    point st,ed,v;
}l[N];
vector<point>p;
vector<double>ans;
int n,tot=-1,first[M],to[M],nxt[M],nxte[M];
bool vis[M];

point get_inter(line a,line b)
{
    double s1=b.v^(a.st-b.st);
    double s2=((a.ed-b.st)^b.v)+s1;
    return a.st+a.v*(s1/s2);
}

int get_id(const point &a)
{
    return lower_bound(p.begin(),p.end(),a)-p.begin();
}

void add(int x,int y)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y;
}

void Init()
{
    memset(first,-1,sizeof(first));
    memset(nxte,-1,sizeof(nxte));
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%lf%lf%lf%lf",&l[i].st.x,&l[i].st.y,&l[i].ed.x,&l[i].ed.y);
        l[i].v=(l[i].ed-l[i].st);
    }
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
            if(abs(l[i].v^l[j].v)>eps)
                p.push_back(get_inter(l[i],l[j]));
    sort(p.begin(),p.end());
    p.erase(unique(p.begin(),p.end()),p.end());
    for(int i=0;i<n;i++)
    {
        vector<double>dis;
        point d=l[i].v.unit();
        for(int j=0;j<n;j++)
            if(abs(l[i].v^l[j].v)>eps)
            {
                point inter=get_inter(l[i],l[j]);
                dis.push_back((inter-l[i].st)*d);
            }
        sort(dis.begin(),dis.end());
        dis.erase(unique(dis.begin(),dis.end()),dis.end());
        for(int j=1,sz=dis.size();j<sz;j++)
        {
            int a=get_id(l[i].st+d*dis[j]);
            int b=get_id(l[i].st+d*dis[j-1]);
            add(a,b),add(b,a);
        }
    }
    for(int i=0,sz=p.size();i<sz;i++)
    {
        vector<pair<double,int> >P;
        for(int e=first[i];e!=-1;e=nxt[e])
            P.push_back(make_pair((p[to[e]]-p[i]).ang(),e));
        sort(P.begin(),P.end());
        for(int j=0,sz=P.size();j<sz;j++)
            nxte[P[(j+1)%sz].second^1]=P[j].second;
    }
}

void work()
{
    for(int i=0;i<=tot;i++)
    {
        if(vis[i])continue;
        stack<int>s;
        s.push(i),vis[i]=true;
        int j=nxte[i];
        while(!vis[j])
        {
            if(!s.empty()&&(s.top()^1)==j)s.pop();
            else s.push(j);
            vis[j]=true;
            j=nxte[j];
            if(j==-1)break;
        }
        if(i==j)
        {
            double area=0;
            while(!s.empty())
            {
                area+=(p[to[s.top()^1]]^p[to[s.top()]]);
                s.pop();
            }
            area*=0.5;
            if(area>0)ans.push_back(area);
        }
    }
}

int main()
{
    //freopen("lx.in","r",stdin);
    Init();
    work();
    printf("%d\n", ans.size());
    sort(ans.begin(), ans.end());
    for(int i=0,sz=ans.size();i<sz;++i)
        printf("%.4lf\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值