【题目描述】:
平面上有N个点,Xaviera想找出周长最小的三角形。
由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题。
为了减小问题的难度,这里的三角形也包括共线的三点。
【题目解法】:
我们可以用类似分治法求最近点对的方法去做
对点集按照x坐标排序
首先设计solve(l,r)表示求[l,r]间的点构成的最小周长三角形
然后我们分治solve(l,mid) solve(mid+1,r)
现在我们就要考虑处理两边的点共同构成的最小周长三角形
我们知道在该题中三角形内任意两边之和大于等于第三边
所以假设我们现在的最有答案为ans,那么任意一条边的长度不得超过ans/2才可能成为最优解
所以我们以a[mid].x作为中间的基准,向两边各延伸ans/2个单位,只有这些点才可能构成最优解
然后我们把这些点按照y坐标排序
如果两个点的y坐标之差>ans/2那么显然也不能成为最优解
然后据说这样每个点最多和16个点进行判断
所以复杂度就是O(n log n log n * 16 * 16)
当然内部排序可以归并做到O(n log n * 16 * 16)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define sqr(x) ((x)*(x))
using namespace std;
const int N=200005;
const double INF=320832083208.8;
struct point {double x,y;} a[N],b[N];
int n; double ans;
inline long long getint()
{
long long x=0; char c=getchar(); bool flag=false;
while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
if (c=='-') flag=true,c=getchar();
while ((c>='0')&&(c<='9')) x=x*10LL+(long long)(c-'0'),c=getchar();
if (flag) return -x; else return x;
}
inline double dist(const point &a,const point &b)
{
return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
inline bool cmpx(const point &a,const point &b)
{
return a.x<b.x;
}
inline bool cmpy(const point &a,const point &b)
{
return a.y<b.y;
}
inline double tangle(const point &a,const point &b,const point &c)
{
return dist(a,b)+dist(a,c)+dist(b,c);
}
void init()
{
n=getint(); ans=INF;
for (int i=1; i<=n; i++) a[i].x=getint(),a[i].y=getint();
sort(a+1,a+n+1,cmpx);
}
void solve(int l,int r)
{
if (l+1>=r) return;
if (l+2==r) {ans=min(ans,tangle(a[l],a[l+1],a[r])); return;}
int mid=(l+r)/2; solve(l,mid); solve(mid+1,r);
double nowans=ans/2.0; int tot=0;
for (int i=l; i<=r; i++) if (fabs(a[i].x-a[mid].x)<=nowans) b[++tot]=a[i];
sort(b+1,b+tot+1,cmpy);
for (int i=1; i<=tot; i++)
for (int j=i+1; j<=tot; j++)
{
if (b[j].y-b[i].y>nowans) break;
for (int k=j+1; k<=tot; k++)
{
if (b[k].y-b[i].y>nowans) break;
ans=min(ans,tangle(b[i],b[j],b[k]));
}
}
}
int main()
{
//freopen("1.in","r",stdin);
init();
solve(1,n);
printf("%.6lf\n",ans);
return 0;
}