• [2019HDU多校第四场][HDU 6617][D. Enveloping Convex]


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6617

    题目大意:给出一凸包(P),求最小的与(P)相似且对应边平行的多边形,使得题目给出的(m)个点(q_i)都被该多边形包含在内,输出最小相似比

    题解:二分答案(k),考虑如何判断(P)被放大(k)倍后是否可以通过平移这(m)个点使他们都在多边形内。将多边形的所有边看成有向线段(逆时针),则(m)个点都在多边形内当且仅当他们都在这些有向线段的左侧。对第(i)条边记录(f_i)表示如果点(q_{f_i})在这条边左侧,就能保证所有点都在其左侧,通过对这(m)个点求凸包后可以在(O(m))的时间复杂度内求出(f_i)。这样我们就只需要知道,是否存在一个平移的向量,使得这(m)个点平移之后,所有的点(q_{f_i})都在第(i)条边的左侧。

       于是我们如果对每条边((p_i,p_{i+1})),求出了符合条件的向量集(vec{A_i}),就可以通过判断他们的交集是否为空来判断当前的比例(k)是否合法。可以发现(vec{A_i}={vec{OA}|A ext{ is on the left of }vec{(p_i-q_{f_i},p_{i+1}-q_{f_i})}})代表了一个半平面,这时我们就可以通过求半平面交来解决这道题了。

       注意题目的要求是对应边平行,因此我们还要把原图形旋转(180^{circ})再做一遍,取两次答案的最小值输出。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100001
    const double eps=1e-10;
    int sgn(double x)
    {
        if(x<-eps)return -1;
        if(x>eps)return 1;
        return 0;
    }
    struct Point
    {
        double x,y;
        void read(){scanf("%lf%lf",&x,&y);}
        Point operator +(const Point &t)const{return {x+t.x,y+t.y};}
        Point operator -(const Point &t)const{return {x-t.x,y-t.y};}
        double operator *(const Point &t)const{return x*t.y-y*t.x;}
        Point operator *(const double t)const{return {x*t,y*t};}
        Point operator /(const double t)const{return {x/t,y/t};}
        double ang()const{return atan2(1.0*y,1.0*x);}
        double length()const{return sqrt(x*x+y*y);}
    }p[N];
    struct Line
    {
        Point p1,p2;
        Point isct(const Line &t)const
          {
          double a=(p2-p1)*(t.p1-p1);
          double b=(p2-p1)*(p1-t.p2);
          return (t.p1*b+t.p2*a)/(a+b);
          }
    }q[N],line[N];
    Point cent;
    bool cmpang(const Point &p1,const Point &p2)
    {
        int tmp=sgn((p1-cent).ang()-(p2-cent).ang());
        if(tmp!=0)return tmp<0;
        return (p1-cent).length()<(p2-cent).length();
    }
    struct Polygon
    {
        int n;
        Point a[N];
        void read()
          {
          scanf("%d",&n);
          for(int i=1;i<=n;i++)
            a[i].read();
          }
        void ChangetoConvex()
          {
          for(int i=2;i<=n;i++)
            if(a[i].x<a[1].x || (a[i].x==a[1].x && a[i].y<a[1].y))
              swap(a[1],a[i]);
          cent=a[1];
          sort(a+2,a+n+1,cmpang);
          int top=2;
          for(int i=3;i<=n;i++)
            {
            while(top>=2 && (a[top]-a[top-1])*(a[i]-a[top])<=0)top--;
            a[++top]=a[i];
            }
          n=top;
          }
    }P,Q;
    int T,nxt[N],pre[N],f[N];
    bool check(int i,Point A,Point B)
    {
        return sgn((B-A)*(Q.a[pre[i]]-Q.a[i]))>=0 && sgn((B-A)*(Q.a[nxt[i]]-Q.a[i]))>=0;
    }
    bool Left(const Point &p,const Line &l){return sgn((l.p2-l.p1)*(p-l.p1))==1;}
    bool HalfPlane(int n)
    {
        int h=1,t=1;
        q[1]=line[1];
        for(int i=2;i<=n;i++)
          {
          while(h<t && !Left(p[t-1],line[i]))t--;
          while(h<t && !Left(p[h],line[i]))h++;
          if(sgn((q[t].p2-q[t].p1)*(line[i].p2-line[i].p1))==0)
            q[t]=Left(q[t].p1,line[i])?q[t]:line[i];
          else q[++t]=line[i];
          if(h<t)p[t-1]=q[t].isct(q[t-1]);
          }
        while(h<t && !Left(p[t-1],q[h]))t--;
        if(t-h<=1)return false;
        p[t]=q[t].isct(q[h]);
        double ans=0;
        for(int i=h;i<=t;i++)
          ans+=p[i]*p[(i+1-h)%(t-h+1)+h];
        return sgn(ans)>=0;
    }
    bool check(double k)
    {
        for(int i=1;i<=P.n;i++)
          line[i]={P.a[i]*k-Q.a[f[i]],P.a[i%P.n+1]*k-Q.a[f[i]]};
        return HalfPlane(P.n);
    }
    double solve()
    {
        int j=1;
        for(int i=1;i<=P.n;i++)
          {
          while(!check(j,P.a[i],P.a[i%P.n+1]))j=nxt[j];
          f[i]=j;
          }
        double l=0,r=1e9,mid;
        while(l+eps<r)
          {
          mid=(l+r)*0.5;
          if(check(mid))r=mid;
          else l=mid;
          }
        return l;
    }
    void init()
    {
        double ans=1e18;
        P.read(),Q.read();
        if(Q.n==1){printf("%.6f
    ",0.0);return;}
        P.ChangetoConvex();
        Q.ChangetoConvex();
        for(int i=1;i<=Q.n;i++)
          nxt[i]=i%Q.n+1,pre[i%Q.n+1]=i;
        ans=min(ans,solve());
        for(int i=1;i<=P.n;i++)
          P.a[i]=P.a[i]*(-1.0);
        ans=min(ans,solve());
        printf("%.6f
    ",ans);
    }
    int main()
    {
        scanf("%d",&T);
        while(T--)init();
    }
    View Code

       关于(vec{A_i})的说明:设点(q_{f_i})为(Q),则(vec{OA})满足原点(O)在平移(vec{OA})后,他在有向线段(vec{(p_i-Q,p_{i+1}-Q)})的左侧。所以原点(O)在平移(vec{OA})后,再平移(vec{OQ}),就能满足其在有向线段(vec{(p_i,p_{i+1})})的左侧。而(O+vec{OA}+vec{OQ}=O+vec{OQ}+vec{OA}=Q+vec{OA}),因此将(Q)平移(vec{OA})是能满足要求的。

  • 相关阅读:
    SPOJ 149 FSHEEP Fencing in the Sheep ( 计算几何 + 二分 )
    UVa 11806
    UVa 1445
    HDU 4725 The Shortest Path in Nya Graph( 建图 + 最短路 )
    HDU 4661 Message Passing ( 树DP + 推公式 )
    从远程库克隆库
    添加远程库
    远程仓库
    删除文件
    撤销修改
  • 原文地址:https://www.cnblogs.com/DeaphetS/p/11300827.html
Copyright © 2020-2023  润新知