bzoj2458 最小三角形

Description

Xaviera现在遇到了一个有趣的问题。
平面上有N个点,Xaviera想找出周长最小的三角形。
由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题。
为了减小问题的难度,这里的三角形也包括共线的三点。

Input

第一行包含一个整数N表示点的个数。
接下来N行每行有两个整数,表示这个点的坐标。

Output

输出只有一行,包含一个6位小数,为周长最短的三角形的周长(四舍五入)。

首先旋转坐标系防止被卡。
将点排序,分割为左右/上下两部分,对每个子问题分别排序,直至子问题足够小时暴力枚举。
利用子问题求得的当前最优解ans缩小范围,将两个子问题在分界直线两侧距离<ans/2的点进行枚举。
#include<cstdio>
#include<algorithm>
#include<cmath>
double ans=1.0e16;
int w=0;
struct pos{
    double x[2];
}ps[200010];
bool operator<(const pos &a,const pos &b){
    return a.x[w]<b.x[w];
}
double dist(pos &a,pos &b){
    double x=a.x[0]-b.x[0],y=a.x[1]-b.x[1];
    return sqrt(x*x+y*y);
}
void f(int _w,int l,int r){
    if(l+20>r){
        for(int i=l;i<r;i++)
        for(int j=i+1;j<r;j++)
        for(int k=j+1;k<=r;k++){
            double v=dist(ps[i],ps[j])+dist(ps[i],ps[k])+dist(ps[j],ps[k]);
            if(v<ans)ans=v;
        }
        return;
    }
    w=_w;
    std::sort(ps+l,ps+r+1);
    int m=l+r>>1;
    f(_w^1,l,m);
    f(_w^1,m+1,r);
    w=_w;
    std::sort(ps+l,ps+m+1);
    std::sort(ps+m+1,ps+r+1);
    int p1=m,p2=m+1;
    int a=ans*0.5+1,x1=ps[m].x[_w],x2=ps[m+1].x[_w];
    while(p1>l&&ps[p1-1].x[_w]+a>=x1)--p1;
    while(p2<r&&ps[p2+1].x[_w]-a<=x2)++p2;
    w^=1;
    std::sort(ps+p1,ps+m+1);
    std::sort(ps+m+1,ps+p2+1);
    for(int i=p1;i<=m;i++){
    int p3=m+1,p4=p2;
    while(p3<p2&&ps[p3+1].x[w]+a<ps[i].x[w])++p3;
    while(p4>m+1&&ps[p4-1].x[w]-a>ps[i].x[w])--p4;
    for(int j=p3;j<p4;j++)
    for(int k=j+1;k<=p4;k++){
        double v=dist(ps[i],ps[j])+dist(ps[i],ps[k])+dist(ps[j],ps[k]);
        if(v<ans)ans=v;
    }
    }
    for(int i=m+1;i<=p2;i++){
    int p3=p1,p4=m;
    while(p3<p2&&ps[p3+1].x[w]+a<ps[i].x[w])++p3;
    while(p4>m+1&&ps[p4-1].x[w]-a>ps[i].x[w])--p4;
    for(int j=p3;j<p4;j++)
    for(int k=j+1;k<=p4;k++){
        double v=dist(ps[i],ps[j])+dist(ps[i],ps[k])+dist(ps[j],ps[k]);
        if(v<ans)ans=v;
    }
    }
}
int n;
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%lf%lf",&ps[i].x[0],&ps[i].x[1]);
    double c=cos(0.8),s=sin(0.8);
    for(int i=0;i<n;i++){
        double x=ps[i].x[0],y=ps[i].x[1];
        ps[i].x[0]=c*x+s*y;
        ps[i].x[1]=-s*x+c*y;
    }
    f(0,0,n-1);
    printf("%.6lf",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/ccz181078/p/5250467.html