bzoj 1020 计算几何

题意:一堆多边形,一个折线。求折线上的点离多边形最远距离。

听说莫涛的论文写了这道题?

先把折线中所有的线段用多边形截,把不在多边形内的线段塞到一个队列里。
遍历队列,对于一个线段求出左端点到多边形的最近点p1,右端点到多边形的最近点p2。

分别用左右端点到p1,p2的距离更新答案。在线段上二分一个点p使dis(p,p1)=dis(p,p2)。
如果dis(p,p1)>ans那么把当前线段从p分成两段塞回队列。

并不会证明,但是这玩意跑得飞快。

#include <bits/stdc++.h>
using namespace std;
#define ppi pair<poi,int> 
#define eps 1e-9
#define inf 1e9
int c,n,cnt;
double ans;
const double e=exp(1),pi=acos(-1);
int dcmp(double x)
{
    if(x<-eps)return -1;
    if(x>eps)return 1;
    return 0;
}
struct poi
{
    double x,y;
    poi(){}
    poi(double x,double y):x(x),y(y){}
    friend bool operator < (const poi &p1,const poi &p2)
    {
        if(!dcmp(p1.x-p2.x))return p1.y<p2.y;
        return p1.x<p2.x;
    }
    void read(){scanf("%lf%lf",&x,&y);}
    friend poi operator - (const poi &p1,const poi &p2)
    {return poi(p1.x-p2.x,p1.y-p2.y);};
    friend poi operator + (const poi &p1,const poi &p2)
    {return poi(p1.x+p2.x,p1.y+p2.y);};
    friend double operator * (const poi &p1,const poi &p2)
    {return p1.x*p2.x+p1.y*p2.y;};
    friend double operator ^ (const poi &p1,const poi &p2)
    {return p1.x*p2.y-p2.x*p1.y;};
    friend poi operator * (double x,const poi &p1)
    {return poi(p1.x*x,p1.y*x);};
}v[2100];
double dis(poi p1,poi p2)
{return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);}
struct line
{
    poi p1,p2,v;
    line(){}
    line(poi p1,poi p2):p1(p1),p2(p2){v=p1-p2;}
    poi mid(){return poi((p1.x+p2.x)/2,(p1.y+p2.y)/2);}
}a[51];
queue<line>que;
int online(line l1,poi p1)
{return dcmp((p1-l1.p1)*(p1-l1.p2))<=0;}
ppi intersect(line l1,line l2)
{
    l1.v=l1.p1-l1.p2;
    l2.v=l2.p1-l2.p2;
    poi u=l1.p1-l2.p1;
    double tmp=(u^l2.v)/(l2.v^l1.v);
    poi p=l1.p1+tmp*l1.v;
    return make_pair(p,online(l1,p)&&online(l2,p));
}
struct poly
{
    int m;
    poi p[51];
    line l[51];
    void read()
    {
        scanf("%d",&m);
        for(int i=1;i<=m;i++)p[i].read();
        p[m+1]=p[1];
        for(int i=1;i<=m;i++)l[i]=line(p[i],p[i+1]);
    }
    void cal(line l1)
    {
        for(int i=1;i<=m;i++)
        {   
            ppi p1=intersect(l[i],l1);
            if(p1.second)v[++cnt]=p1.first;
        }
    }
    int in(poi p1)
    {
        int cnt=0;
        line l1=line(p1,poi(p1.x+e*inf,p1.y+pi*inf));
        for(int i=1;i<=m;i++)
            cnt+=intersect(l1,l[i]).second;
        return cnt&1;
    }
    poi get(poi p1)
    {
        poi ret=poi(inf,inf);
        for(int i=1;i<=m;i++)
        {
            ret=dis(ret,p1)<dis(l[i].p1,p1) ? ret:l[i].p1;
            ret=dis(ret,p1)<dis(l[i].p2,p1) ? ret:l[i].p2;
            poi p2=poi(p1.x-l[i].p2.y+l[i].p1.y,p1.y+l[i].p2.x-l[i].p1.x);
            ppi p=intersect(l[i],line(p1,p2));
            if(online(l[i],p.first))
                ret=dis(ret,p1)<dis(p.first,p1) ? ret:p.first;
        }
        return ret;
    }
}b[51];
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d%d",&c,&n);
    for(int i=1;i<n;i++)a[i].p1.read();
    a[n-1].p2.read();
    for(int i=1;i<n-1;i++)a[i].p2=a[i+1].p1;
    for(int i=1;i<=c;i++)b[i].read();
    for(int i=1;i<n;i++)
    {
        v[cnt=1]=a[i].p1;
        for(int j=1;j<=c;j++)
            b[j].cal(a[i]);
        v[++cnt]=a[i].p2;
        sort(v+1,v+1+cnt);
        for(int j=1;j<cnt;j++)
            if(dcmp(dis(v[j],v[j+1]))>0)
            {
                int flag=0;
                for(int k=1;k<=c;k++)
                    if(b[k].in(line(v[j],v[j+1]).mid()))
                        {flag=1;break;} 
                if(!flag)que.push(line(v[j],v[j+1]));
            }
    }
    while(!que.empty())
    {
        line l1=que.front();
        que.pop();
        poi p1=poi(inf,inf),p2=p1;
        for(int i=1;i<=c;i++)
        {   
            poi p=b[i].get(l1.p1);
            p1=dis(p1,l1.p1)<dis(p,l1.p1) ? p1:p;
            p=b[i].get(l1.p2);
            p2=dis(p2,l1.p2)<dis(p,l1.p2) ? p2:p;   
        }
        ans=max(ans,sqrt(dis(p1,l1.p1)));
        ans=max(ans,sqrt(dis(p2,l1.p2)));
        poi l=l1.p1,r=l1.p2;
        while(dcmp(dis(l,r))>0)
        {
            poi mid=line(l,r).mid();
            if(dis(mid,p1)<dis(mid,p2))l=mid;
            else r=mid;
        }
        if(sqrt(dis(l,p1))>ans+0.001)
        {
            que.push(line(l,l1.p1));
            que.push(line(l,l1.p2));
        }
    }
    printf("%.2lf\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值