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; }