LA3218 Find the Border

题意

PDF

分析

虽然只找外轮廓,但是时间复杂度不比PSLG优秀,所以可以当做联系PSLG的题做。

PSLG框架

  1. 找出所有交点
  2. 交点按序连边
  3. 把边按极角序排序
  4. 逆时针找圈

然后何以会顺时针找出无限区域的边呢?缘于这一段:

for(int i=0;i<d;++i)
    prev[G[u][(i+1)%d]]=G[u][i];

prev连向极角序次小的。极角序最小的边的prev连向极角序最大的。

然后在边界上就会顺时针找下去。但是在内部的时候这些边无疑会被逆时针遍历到,所以没有影响。

于是找出无限区域的轮廓的特殊性在于它的有向面积是负的。

时间复杂度主要是找轮廓时遍历边的\(O(n^2)\)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>T read(T&x)
{
    return x=read<T>();
}
using namespace std;
typedef long long ll;

co double eps=1e-8;

int dcmp(double x)
{
    return fabs(x)<eps?0:(x<0?-1:1);
}

struct Point
{
    double x,y;
    
    Point(double x=0,double y=0)
    :x(x),y(y){}
    
    bool operator<(co Point&p)co
    {
        return dcmp(x-p.x)<0||(dcmp(x-p.x)==0&&dcmp(y-p.y)<0);
    }
    
    bool operator==(co Point&p)co
    {
        return dcmp(x-p.x==0)&&dcmp(y-p.y)==0;
    }
};
typedef Point Vector;

Vector operator+(co Vector&A,co Vector&B)
{
    return Vector(A.x+B.x,A.y+B.y);
}

Vector operator-(co Vector&A,co Vector&B)
{
    return Vector(A.x-B.x,A.y-B.y);
}

Vector operator*(co Vector&A,double p)
{
    return Vector(A.x*p,A.y*p);
}

double Dot(co Vector&A,co Vector&B)
{
    return A.x*B.x+A.y*B.y;
}

double Cross(co Vector&A,co Vector&B)
{
    return A.x*B.y-A.y*B.x;
}

double Length(co Vector&A)
{
    return sqrt(Dot(A,A));
}

typedef vector<Point> Polygon;

Point LineLineIntersection(co Point&P,co Vector&v,co Point&Q,co Vector&w)
{
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}

bool SegmentProperIntersection(co Point&a1,co Point&a2,co Point&b1,co Point&b2)
{
    double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1),
           c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1);
    return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}

bool OnSegment(co Point&p,co Point&a1,co Point&a2)
{
    return dcmp(Cross(a1-p,a2-p))==0&&dcmp(Dot(a1-p,a2-p))<0;
}

double PolygonArea(co Polygon&poly)
{
    double area=0;
    int n=poly.size();
    for(int i=1;i<n-1;++i)
        area+=Cross(poly[i]-poly[0],poly[(i+1)%n]-poly[0]);
    return area/2;
}

struct Edge
{
    int from,to;
    double ang;
};

co int MAXN=1e4;

struct PSLG
{
    int n,m,face_cnt;
    double x[MAXN],y[MAXN];
    vector<Edge>edges;
    vector<int>G[MAXN];
    int vis[MAXN*2];
    int left[MAXN*2];
    int prev[MAXN*2];
    
    vector<Polygon>faces;
    double area[MAXN];
    
    void Init(int n)
    {
        this->n=n;
        for(int i=0;i<n;++i)
            G[i].clear();
        edges.clear();
        faces.clear();
    }
    
    double Angle(int from,int to)
    {
        return atan2(y[to]-y[from],x[to]-x[from]);
    }
    
    void AddEdge(int from,int to)
    {
        edges.push_back((Edge){from,to,Angle(from,to)});
        edges.push_back((Edge){to,from,Angle(to,from)});
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    
    void Build()
    {
        for(int u=0;u<n;++u)
        {
            int d=G[u].size();
            for(int i=0;i<d;++i)
                for(int j=i+1;j<d;++j)
                    if(edges[G[u][i]].ang>edges[G[u][j]].ang)
                        swap(G[u][i],G[u][j]);
            for(int i=0;i<d;++i)
                prev[G[u][(i+1)%d]]=G[u][i];
        }
        
        fill(vis,vis+m,0);
        face_cnt=0;
        for(int u=0;u<n;++u)
            for(int i=0;i<G[u].size();++i)
            {
                int e=G[u][i];
                if(!vis[e])
                {
                    face_cnt++;
                    Polygon poly;
                    for(;;)
                    {
                        vis[e]=1;
                        left[e]=face_cnt;
                        int from=edges[e].from;
                        poly.push_back(Point(x[from],y[from]));
                        e=prev[e^1];
                        if(e==G[u][i])
                            break;
                        assert(vis[e]==0);
                    }
                    faces.push_back(poly);
                }
            }
        
        for(int i=0;i<faces.size();++i)
        {
            area[i]=PolygonArea(faces[i]);
        }
    }
}g;

co int MAXP=100;
int n,c;
Point P[MAXP];

Point V[MAXP*(MAXP-1)/2+MAXP];

int ID(Point P)
{
    return lower_bound(V,V+c,P)-V;
}

Polygon Simplify(co Polygon&poly)
{
    Polygon ans;
    int n=poly.size();
    for(int i=0;i<n;++i)
    {
        co Point&a=poly[i];
        co Point&b=poly[(i+1)%n];
        co Point&c=poly[(i+2)%n];
        if(dcmp(Cross(a-b,c-b))!=0)
            ans.push_back(b);
    }
    return ans;
}

void BuildGraph()
{
    c=n;
    for(int i=0;i<n;++i)
        V[i]=P[i];
        
    vector<double>dist[MAXP];
    for(int i=0;i<n;++i)
        for(int j=i+1;j<n;++j)
            if(SegmentProperIntersection(P[i],P[(i+1)%n],P[j],P[(j+1)%n]))
            {
                Point p=LineLineIntersection(P[i],P[(i+1)%n]-P[i],P[j],P[(j+1)%n]-P[j]);
                V[c++]=p;
                dist[i].push_back(Length(p-P[i]));
                dist[j].push_back(Length(p-P[j]));
            }
            
    sort(V,V+c);
    c=unique(V,V+c)-V;
    
    g.Init(c);
    for(int i=0;i<c;++i)
    {
        g.x[i]=V[i].x;
        g.y[i]=V[i].y;
    }
    for(int i=0;i<n;++i)
    {
        Vector v=P[(i+1)%n]-P[i];
        double len=Length(v);
        dist[i].push_back(0);
        dist[i].push_back(len);
        sort(dist[i].begin(),dist[i].end());
        int sz=dist[i].size();
        for(int j=1;j<sz;++j)
        {
            Point a=P[i]+v*(dist[i][j-1]/len);
            Point b=P[i]+v*(dist[i][j]/len);
            if(a==b)
                continue;
            g.AddEdge(ID(a),ID(b));
        }
    }
    
    g.Build();
    
    Polygon poly;
    for(int i=0;i<g.faces.size();++i)
        if(g.area[i]<0)
        {
            poly=g.faces[i];
            reverse(poly.begin(),poly.end());
            poly=Simplify(poly);
            break;
        }
    
    int m=poly.size();
    printf("%d\n",m);
    
    int start=0;
    for(int i=0;i<m;++i)
        if(poly[i]<poly[start])
            start=i;
    for(int i=start;i<m;++i)
        printf("%.4lf %.4lf\n",poly[i].x,poly[i].y);
    for(int i=0;i<start;++i)
        printf("%.4lf %.4lf\n",poly[i].x,poly[i].y);
}

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;++i)
        {
            int x,y;
            read(x),read(y);
            P[i]=Point(x,y);
        }
        BuildGraph();
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10178565.html