您的位置:首页 > 其它

bzoj 2458: [BeiJing2011]最小三角形 分治

2016-12-06 21:42 337 查看

题意

给出平面上的n个点,求三个点使得这三个点的两两距离和最小。

n<=200000

分析

这题的方法可以借鉴平面最近点对的求法

先把点按照横坐标排序。考虑分治,对于一个区间[1,n],往两边分治。然后答案分三种情况,一种是在左区间中,一种是在右区间中,最后一种是跨过了两个区间。显然前两种我们都已经处理完毕了,接着考虑第三种。分两种情况讨论,一种是两个点在左区间,一种是两个点在右区间。假设现在左右两个区间的最小距离和为ans,那么候选的点与两个区间的分割线的距离必然不超过ans/2。对于左区间的一个点,如要在右区间找两个点组成距离最小的三个点,那么右区间的两个点的纵坐标与该点的纵坐标差值的绝对值必然不超过ans/2,那么我们就可以用双指针来维护满足条件的点,然后暴力查找即可。每次处理完之后可以把左右区间的点按照y值归并排序,这样就可以避免掉sort带来的多一个log。

一开始蜜汁WA,后来在dist函数中加了*1.0就A了……真的是,精度害死人啊。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200005
#define inf 0x7fffffff
using namespace std;

int n,a1,b1,a
,b
;
double ans;
struct pts{int x,y;}p
,tmp
;

int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

bool cmpx(pts a,pts b)
{
return a.x<b.x;
}

bool cmpy(pts a,pts b)
{
return a.y<b.y;
}

double dist(int x,int y)
{
return sqrt((p[x].x-p[y].x)*1.0*(p[x].x-p[y].x)+(p[x].y-p[y].y)*1.0*(p[x].y-p[y].y));
}

void merge(int l,int r)
{
int mid=(l+r)/2;
int l1=l,r1=mid+1,now=l;
while (l1<=mid&&r1<=r)
if (p[l1].y<p[r1].y)
{
tmp[now++]=p[l1];
l1++;
}else
{
tmp[now++]=p[r1];
r1++;
}
for (int i=l1;i<=mid;i++)
tmp[now++]=p[i];
for (int i=r1;i<=r;i++)
tmp[now++]=p[i];
for (int i=l;i<=r;i++)
p[i]=tmp[i];
}

void solve(int l,int r)
{
if (r==l) return;
int mid=(l+r)/2,midx=p[mid+1].x;
solve(l,mid);solve(mid+1,r);
a1=b1=0;
for (int i=l;i<=mid;i++)
if (midx-p[i].x<=ans/2) a[++a1]=i;
for (int i=mid+1;i<=r;i++)
if (p[i].x-midx<=ans/2) b[++b1]=i;
int l1=1,r1=0;
for (int i=1;i<=b1;i++)
{
while (l1<=a1&&p[b[i]].y-p[a[l1]].y>ans/2) l1++;
if (l1>a1) break;
r1=max(r1,l1-1);
while (r1<a1&&abs(p[a[r1+1]].y-p[b[i]].y)<=ans/2) r1++;
if (l1>=r1) continue;
for (int j=l1;j<r1;j++)
for (int k=j+1;k<=r1;k++)
ans=min(ans,dist(a[j],a[k])+dist(a[j],b[i])+dist(a[k],b[i]));
}
l1=1;r1=0;
for (int i=1;i<=a1;i++)
{
while (l1<=b1&&p[a[i]].y-p[b[l1]].y>ans/2) l1++;
if (l1>b1) break;
r1=max(r1,l1-1);
while (r1<b1&&abs(p[b[r1+1]].y-p[a[i]].y)<=ans/2) r1++;
if (l1>=r1) continue;
for (int j=l1;j<r1;j++)
for (int k=j+1;k<=r1;k++)
ans=min(ans,dist(b[j],b[k])+dist(b[j],a[i])+dist(b[k],a[i]));
}
merge(l,r);
}

int main()
{
n=read();
for (int i=1;i<=n;i++)
{
p[i].x=read();p[i].y=read();
}
sort(p+1,p+n+1,cmpx);
ans=inf;
solve(1,n);
printf("%.6lf",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: