BZOJ2458:[BJOI2011]最小三角形——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=2458
Description
Xaviera现在遇到了一个有趣的问题。
平面上有N个点,Xaviera想找出周长最小的三角形。
由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题。
为了减小问题的难度,这里的三角形也包括共线的三点。
Input
第一行包含一个整数N表示点的个数。
接下来N行每行有两个整数,表示这个点的坐标。
Output
输出只有一行,包含一个6位小数,为周长最短的三角形的周长(四舍五入)。
Sample Input
4
1 1
2 3
3 3
3 4
1 1
2 3
3 3
3 4
Sample Output
3.414214
HINT
100%的数据中N≤200000。
————————————————
哇作为练手平面分治的题我好高兴,一次A了。
不过最开始怀疑自己的思路有问题于是查了题解……然后发现竟然是对的。
额……
复述一下,和平面分治差不多,将solve函数的含义改为当前区间内最小三角形的周长。
然后我们发现对于一个三角形两点之间长度最大为d/2。
所以我们在归并排序y的时候把和中轴距离d/2的点捡出来。
然后三重循环枚举搞定(同时距离d/2不要忘了)
#include<cmath> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef double dl; const dl INF=1e20; const int N=200001; inline int read(){ int X=0,w=0; char ch=0; while(!isdigit(ch)) {w|=ch=='-';ch=getchar();} while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ dl x; dl y; }p[N],a[N],b[N]; bool cmp(node A,node B){ return A.x<B.x; } inline dl dis(int i,int j){ return sqrt(pow(b[i].x-b[j].x,2)+pow(b[i].y-b[j].y,2)); } dl solve(int l,int r){ if(l>=r)return INF; int mid=(l+r)>>1; dl x0=(p[mid].x+p[mid+1].x)/2.0; dl d=min(solve(l,mid),solve(mid+1,r)); int l1=l,r1=mid+1,num=0; for(int i=l;i<=r;i++){ if(l1<=mid&&(r1>r||p[l1].y<p[r1].y)){ a[i]=p[l1++]; if(x0-d/2<a[i].x)b[++num]=a[i]; }else{ a[i]=p[r1++]; if(a[i].x<x0+d/2)b[++num]=a[i]; } } for(int i=l;i<=r;i++)p[i]=a[i]; for(int i=1;i<=num;i++){ for(int j=i+1;j<=num;j++){ if(b[j].y-b[i].y>=d/2)break; for(int k=j+1;k<=num;k++){ if(b[k].y-b[j].y>=d/2)break; d=min(d,dis(i,j)+dis(j,k)+dis(k,i)); } } } return d; } int main(){ int n=read(); for(int i=1;i<=n;i++){ p[i].x=read(); p[i].y=read(); } sort(p+1,p+n+1,cmp); printf("%.6f\n",solve(1,n)); return 0; }