CodeVS2319 最近最远点对

来源:互联网 发布:wps表格怎么刷新数据 编辑:程序博客网 时间:2024/05/18 02:02

http://codevs.cn/problem/2319/

题意:给定笛卡尔平面内的若干个点,求最近两点的距离和最远两点的距离,距离是欧氏距离,点数不大于100,000。

对于最近点对考虑分治的做法:用一条直线将所有点分成两部分,对于两部分的点进行分治,然后考虑两部分联合的结果。

可以先把所有点按纵坐标排序,这样就容易用平行于x轴的直线划分。设两边分别求出的结果的较小值是d,我们只需考虑直线两侧离直线距离d以内的所有点即可。

考虑枚举直线某一侧的所有点,对于每个点,在直线另一侧到该点距离小于d的点,是有常数性的限度的。

假设这个点离直线非常非常近,或者就在直线上,以该点为圆心,d为半径所作出的圆在直线另一侧的部分是一个劣弓形,或者半圆。

直线另一侧的一个点若要到该点的距离小于d,则必须落在这个弓形或半圆以内。一个小小的半圆里面能有多少个点呢?注意到在这些点中,所有点对的距离不小于d!

最多只有5个点。事实上,具体算法实现非常简单。对前一半点和后一半点进行分治,然后两重循环枚举前一半点和后一半中的前五个点,计算距离并更新即可。

对于最远点对,显然答案中的两个点一定在整个点集的凸包上,凸包可以用Graham算法来求出。然后旋转卡壳即可。

关于旋转卡壳,我们这里要找的就是在凸包上,与每个点距离最远的另一个点,设凸包上的若干个点依次是p[]号点,离p[i]号点最远的点是f[i]号点。

则对于顺时针依次的p[i]和p[i+1],其答案f[i]和f[i+1]也一定按顺时针排列,这是比较显然的,于是可以用单调队列来求出f[],选出最远的一对即可。

代码:

#include<cstdio>#include<iostream>#include<cstring>#include<string>#include<algorithm>#include<map>#include<set>#include<bitset>#include<queue>#include<stack>#include<cmath>#include<climits>#define rpt(i,l,r) for(i=l;i<=r;i++)#define rpd(i,r,l) for(i=r;i>=l;i--);#define maxn 100005using namespace std;struct point{       double x,y;};point a[maxn],b[maxn*2];int n,i;double ans1=INT_MAX,ans2=0;bool cmp1(point A,point B){     return A.y<B.y;}bool clockwise(point A,point B,point C){     return A.x*B.y+B.x*C.y+C.x*A.y-A.y*B.x-B.y*C.x-C.y*A.x<0;}bool cmp2(point A,point B){     return clockwise(A,a[1],B);}double dist(point A,point B){       double distx=A.x-B.x,disty=A.y-B.y;       return sqrt(distx*distx+disty*disty);}void input(){     scanf("%d",&n);     rpt(i,1,n) scanf("%lf%lf",&a[i].x,&a[i].y);}void solve1(int l,int r){     if(l<r){             int m=l+r>>1,i,j,k;             double tmp;             solve1(l,m);             solve1(m+1,r);             rpt(i,l,m) rpt(j,m+1,min(m+5,r)){                        tmp=dist(a[i],a[j]);                        if(tmp<ans1) ans1=tmp;             }     }}void solve2(){     int k=2,j=1;     double tmp;     b[1]=a[1],b[2]=a[2];     rpt(i,3,n){                while(k>1&&clockwise(b[k-1],b[k],a[i])) k--;                b[++k]=a[i];     }     rpt(i,1,k) b[k+i]=b[i];     rpt(i,1,k){                while(dist(b[i],b[j])<dist(b[i],b[j+1])+1e-6) j++;                tmp=dist(b[i],b[j]);                if(tmp>ans2) ans2=tmp;     }}int main(){    input();    sort(a+1,a+n+1,cmp1);    solve1(1,n);    sort(a+2,a+n+1,cmp2);    solve2();    printf("%.2f %.2f\n",ans1,ans2);    return 0;}

0 0
原创粉丝点击