BJ模拟:帐篷(凸包)

传送门

题解:
凸包。

注意 n,m n , m 很大时答案为周长,而周长最小则自动满足凸性,我们只需要枚举起始点即可 O(n2) O ( n 2 ) DP。时间复杂度为 O(n3) O ( n 3 )

n,m n , m 很小的时候我们多记录一个前驱即可。 注意DP要从横纵坐标最小的点开始,不然最后首尾的三个点可能不会满足凸性。

这一部分的时间复杂度为 O(n4) O ( n 4 ) ,预处理的时间复杂度为 O(n2m) O ( n 2 m )


#include <bits/stdc++.h>
using namespace std;
typedef double DB;
#define R(x) ((x)*(x))

inline long long rd() {
    char ch=getchar(); long long i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}

const DB INF=1e50,eps=0;
inline int sgn(DB x) {return (x>eps)-(x<-eps);}
inline int dcmp(DB x,DB y) {return sgn(x-y)<0;}

const int N=4e2+50;
struct P {
    DB x,y,ang; int id;
    P(){}
    P(DB x,DB y,DB ang=0,int id=0):x(x),y(y),ang(ang),id(id){}
    friend inline P operator -(const P &a,const P &b) {return P(a.x-b.x,a.y-b.y);}
    friend inline DB operator *(const P &a,const P &b) {return a.x*b.y-a.y*b.x;}
    friend inline bool operator <(const P &a,const P &b) {return atan2(a.y,a.x)<atan2(b.y,b.x);}
    friend inline DB dis(const P &a,const P &b) {return sqrt(R(a.x-b.x)+R(a.y-b.y));}
}a[N],b[N],c[N];
inline bool cmp_x(const P &a,const P &b) {return a.x<b.x || (a.x==b.x && a.y<b.y);}
inline bool cmp_ang(const P &a,const P &b) {return a.ang<b.ang;}
inline DB cs(int i,int j) {return (a[i]*a[j])*(a[i]*a[j])/(R(a[i].x-a[j].x)+R(a[i].y-a[j].y));}
int n,m,ok[N][N]; DB fib[N],h;
namespace SP1 {
    DB f[N][N], ans;
    inline void calc(int bg) {
        sort(c+bg,c+n+1,cmp_x);
        for(int i=bg+1;i<=n;i++) c[i].ang=atan2(c[i].y-c[bg].y,c[i].x-c[bg].x);
        sort(c+bg+1,c+n+1,cmp_ang);
        for(int i=bg;i<=n;i++)
            for(int j=bg;j<=i;j++)
                f[i][j]=INF;
        f[bg][bg]=0;
        for(int i=bg;i<=n;++i) {
            for(int j=bg;j<i;++j) if(ok[c[j].id][c[i].id])
                for(int k=bg;k!=j+1;++k)
                    if(sgn((c[i]-c[k])*(c[j]-c[k]))<=0) 
                        f[i][j]=min(f[i][j],f[j][k]+dis(c[i],c[j])*sqrt(h*h+cs(c[i].id,c[j].id))/h);
        }
        for(int i=bg+1;i<=n;i++) if(ok[c[i].id][c[bg].id])
            for(int j=bg;j<i;++j)
                if(sgn((c[bg]-c[j])*(c[i]-c[j]))<=0) ans=min(ans,f[i][j]+dis(c[i],c[bg])*sqrt(h*h+cs(c[i].id,c[bg].id))/h);
    }
    inline void solve() {
        h=fib[n+m]; ans=INF;
        for(int i=1;i<=n;i++)  calc(i);
        if(dcmp(ans,INF)) printf("%.3lf\n",ans/2.);
        else puts("NO SOLUTION");
    }
}
namespace SP2 {
    DB f[N], ans;
    inline void calc(int bg) {
        sort(c+bg,c+n+1,cmp_x);
        for(int i=bg+1;i<=n;i++) c[i].ang=atan2(c[i].y-c[bg].y,c[i].x-c[bg].x);
        sort(c+bg+1,c+n+1,cmp_ang);
        for(int i=1;i<=n;i++) f[i]=INF;
        f[bg]=0;
        for(int i=bg+1;i<=n;++i)
            for(int j=bg;j!=i;++j)
                if(ok[c[j].id][c[i].id]) f[i]=min(f[i],f[j]+dis(c[i],c[j]));
        for(int i=bg+1;i<=n;i++) if(ok[c[i].id][c[bg].id]) ans=min(ans,f[i]+dis(c[i],c[bg])); 
    }
    inline void solve() {
        ans=INF;
        for(int i=1;i<=n;i++) calc(i);
        if(dcmp(ans,INF)) printf("%.3lf\n",ans/2.);
        else puts("NO SOLUTION");

    }
}
int main() {
    while(~scanf("%d%d",&n,&m)) {
        fib[1]=1; fib[2]=2;
        for(int i=3;i<=n+m && i<=100; ++i) fib[i]=fib[i-1]+fib[i-2];
        for(int i=1;i<=n;i++) a[i].x=rd(), a[i].y=rd();
        for(int i=1;i<=m;i++) b[i].x=rd(), b[i].y=rd();
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++) 
            for(int j=1;j<=n;j++) if(i!=j) {
                ok[i][j]=1;
                for(int k=1;k<=m&&ok[i][j];k++)
                    if(sgn((b[k]-a[i])*(a[j]-a[i]))>=0) ok[i][j]=0;
            }   
        for(int i=1;i<=n;i++) c[i]=a[i], c[i].id=i;
        if(n+m<=60) SP1::solve();
        else SP2::solve();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值