[bzoj] 2458 最小三角形 || 平面分治
原题
求平面内周长最小的三角形。
平面分治板子题。
设当前最小为d,我们要求跨过分治线的三角形:
把所有距离线不超过d/2的点加入(因为大于d/2那么所在的三角形一定周长大于d,不必考虑),对于每个两个点,考虑同侧距离不超过d/2的点。
而要求在同一侧的三角形只要递归进行即可。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 200010
using namespace std;
int n;
struct hhh
{
double x,y;
bool operator < (const hhh &b) const
{
return x<b.x;
}
friend hhh operator - (const hhh &a,const hhh &b)
{
return (hhh){a.x-b.x,a.y-b.y};
}
inline double ds() const
{
return sqrt(x*x+y*y);
}
}p[N],a[N],b[N],c[N];
double calc(hhh x,hhh y,hhh z)
{
return (x-y).ds()+(y-z).ds()+(z-x).ds();
}
double solve(int l,int r)
{
if (l==r) return 1e20;
int mid=(l+r)>>1;
double mid_x=(p[mid].x+p[mid+1].x)/2;
double d=min(solve(l,mid),solve(mid+1,r));
int pos=l,ll=l,rr=mid+1,b_n=0,c_n=0;
while (pos<=r)
{
if (ll<=mid && (rr>r || p[ll].y<p[rr].y))
{
if (p[ll].x>mid_x-d/2) b[++b_n]=p[ll];
a[pos++]=p[ll++];
}
else
{
if (p[rr].x<mid_x+d/2) c[++c_n]=p[rr];
a[pos++]=p[rr++];
}
}
for (int i=l;i<=r;i++) p[i]=a[i];
if (r-l<2) return 1e20;
for (int i=1,j=1;i<=b_n;i++)
{
while (j<=c_n && b[i].y-c[j].y>d/2) j++;
for (int k=j;k<=c_n && abs(b[i].y-c[k].y)<d/2;k++)
for (int h=k+1;h<=c_n && abs(b[i].y-c[h].y)<d/2;h++)
d=min(d,calc(b[i],c[k],c[h]));
}
for (int i=1,j=1;i<=c_n;i++)
{
while (j<=b_n && c[i].y-b[j].y>d/2) j++;
for (int k=j;k<=b_n && abs(c[i].y-b[k].y)<d/2;k++)
for (int h=k+1;h<=b_n && abs(c[i].y-b[h].y)<d/2;h++)
d=min(d,calc(c[i],b[k],b[h]));
}
return d;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
sort(p+1,p+n+1);
printf("%.6lf\n",solve(1,n));
return 0;
}