[BZOJ2458][BeiJing2011]最小三角形
题目描述 Description |
Xaviera现在遇到了一个有趣的问题。平面上有N个点,Xaviera想找出周长最小的三角形。由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题。为了减小问题的难度,这里的三角形也包括共线的三点。 |
输入描述 Input Description |
第一行包含一个整数N表示点的个数。接下来N行每行有两个整数,表示这个点的坐标。 |
输出描述 Output Description |
输出只有一行,包含一个6位小数,为周长最短的三角形的周长(四舍五入)。 |
样例输入 Sample Input |
4 1 1 2 3 3 3 3 4 |
样例输出 Sample Output |
3.414214 |
数据范围及提示 Data Size & Hint |
之前的一些废话:是时候准备会考了。。
题解:做法类似平面上求最近点对。首先把平面划分成两个部分,递归求出两个部分的答案为ans,然后,把离分割线距离小于ans/2的点全部加入队列,因为只有在这范围内答案才有可能比ans小,加入之后按照y坐标排一遍序,然后滑动窗口维护一下高度为ans/2的一个矩形,然后对矩形内的点暴力选,更新最优解即可。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> using namespace std; typedef long long LL; #define mem(a,b) memset(a,b,sizeof(a)) typedef pair<double,int> PDI; const int maxn=200010; const double oo=2147483647; inline int read() { int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } struct Point { double x,y; Point() {} Point(double _1,double _2):x(_1),y(_2){} bool operator < (const Point &s)const { if(x==s.x)return y<s.y; return x<s.x; } }p[maxn]; int n;double x,y; PDI s[maxn]; double dis(double a,double b,double c,double d){ return sqrt((c-a)*(c-a)+(d-b)*(d-b)); } double triangle_perimeter(double a,double b,double c,double d,double e,double f){ return dis(a,b,c,d)+dis(a,b,e,f)+dis(c,d,e,f); } bool cmp(PDI a,PDI b){return a.first<b.first;} double mdis(int l,int r) { if(l+1>=r)return oo; if(l+2==r)return triangle_perimeter(p[l].x,p[l].y,p[l+1].x,p[l+1].y,p[r].x,p[r].y); int mid=(l+r)>>1,t=0; double d=min(mdis(l,mid),mdis(mid,r)); for(int i=l;i<=r;i++)if(fabs(p[i].x-p[mid].x)<=d/2.0)s[t++]=make_pair(p[i].y,i); sort(s,s+t,cmp); int st=0,ed; while(st<=t-2) { ed=st+1; while(fabs(s[ed].first-s[st].first)<=(d/2.0) && ed<=t-2)ed++; for(int i=st+1;i<ed;i++)for(int j=i+1;j<ed;j++) d=min(d,triangle_perimeter(p[s[st].second].x,p[s[st].second].y,p[s[i].second].x,p[s[i].second].y,p[s[j].second].x,p[s[j].second].y)); st++; } return d; } int main() { n=read(); for(int i=0;i<n;i++)x=(double)read(),y=(double)read(),p[i]=Point(x,y); sort(p,p+n); printf("%.6lf\n",mdis(0,n-1)); return 0; }
总结:滑动窗口好难写。