[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;
}
posted @ 2017-12-13 20:06  Mrha  阅读(638)  评论(0编辑  收藏  举报